/* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "uprobestats" #include #include #include #include #include #include #include #include #include #include #include "Art.h" #include "ConfigResolver.h" #include "DebugLog.h" #include "DynamicInstrumentationManager.h" #include "FlagSelector.h" #include "Process.h" namespace android { namespace uprobestats { namespace config_resolver { std::ostream &operator<<(std::ostream &os, const ResolvedTask &c) { os << "pid: " << c.pid << " taskConfig: " << c.taskConfig.DebugString(); return os; } std::ostream &operator<<(std::ostream &os, const ResolvedProbe &c) { os << "filename: " << c.filename << " offset: " << c.offset << " probeConfig: " << c.probeConfig.DebugString(); return os; } // Reads probing configuration from a file, which should be the serialized // bytes of a UprobestatsConfig proto. std::optional<::uprobestats::protos::UprobestatsConfig> readConfig(std::string configFilePath) { std::string config_str; if (!android::base::ReadFileToString(configFilePath, &config_str)) { LOG(ERROR) << "Failed to open config file " << configFilePath; return {}; } ::uprobestats::protos::UprobestatsConfig config; bool success = config.ParseFromString(config_str); if (!success) { LOG(ERROR) << "Failed to parse file " << configFilePath << " to UprobestatsConfig"; return {}; } return config; } std::optional resolveSingleTask(::uprobestats::protos::UprobestatsConfig config) { auto task_count = config.tasks().size(); if (task_count == 0) { LOG(ERROR) << "config has no tasks"; return {}; } if (task_count > 1) { LOG(ERROR) << "config has " << task_count << " tasks. Only 1 is supported. The first task is read and the " "rest are ignored."; } auto taskConfig = config.tasks().Get(0); if (!taskConfig.has_duration_seconds()) { LOG(ERROR) << "config task has no duration"; return {}; } if (taskConfig.duration_seconds() <= 0) { LOG(ERROR) << "config task cannot have zero or negative duration"; } if (!taskConfig.has_target_process_name()) { LOG(ERROR) << "task.target_process_name is required."; return {}; } auto process_name = taskConfig.target_process_name(); int pid = process::getPid(process_name); if (pid < 0) { LOG(ERROR) << "Unable to find pid of " << process_name; return {}; } ResolvedTask task; task.taskConfig = taskConfig; task.pid = pid; return task; } std::optional> resolveProbes(::uprobestats::protos::UprobestatsConfig::Task &taskConfig) { if (taskConfig.probe_configs().size() == 0) { LOG(ERROR) << "task has no probe configs"; return {}; } std::vector result; for (auto &probeConfig : taskConfig.probe_configs()) { if (android::uprobestats::flag_selector::executable_method_file_offsets() && probeConfig.has_fully_qualified_class_name()) { LOG_IF_DEBUG("using getExecutableMethodFileOffsets to retrieve offsets"); std::vector fqParameters( probeConfig.fully_qualified_parameters().begin(), probeConfig.fully_qualified_parameters().end()); std::string processName(taskConfig.target_process_name()); std::string fqcn(probeConfig.fully_qualified_class_name()); std::string methodName(probeConfig.method_name()); std::optional< dynamic_instrumentation_manager::ExecutableMethodFileOffsets> offsets = dynamic_instrumentation_manager::getExecutableMethodFileOffsets( processName, fqcn, methodName, fqParameters); if (!offsets.has_value()) { LOG(ERROR) << "Unable to find method offset for " << probeConfig.fully_qualified_class_name() << "#" << probeConfig.method_name(); return {}; } ResolvedProbe probe; probe.filename = offsets->containerPath; probe.offset = offsets->methodOffset; probe.probeConfig = probeConfig; result.push_back(probe); continue; } LOG_IF_DEBUG("using oatdump to retrieve offsets"); int offset = 0; std::string matched_file_path; for (auto &file_path : probeConfig.file_paths()) { offset = art::getMethodOffsetFromOatdump(file_path, probeConfig.method_signature()); if (offset > 0) { matched_file_path = file_path; break; } else { LOG(WARNING) << "File " << file_path << " has no offset for " << probeConfig.method_signature(); } } if (offset == 0) { LOG(ERROR) << "Unable to find method offset for " << probeConfig.method_signature(); return {}; } ResolvedProbe probe; probe.filename = matched_file_path; probe.offset = offset; probe.probeConfig = probeConfig; result.push_back(probe); } return result; } } // namespace config_resolver } // namespace uprobestats } // namespace android