blob: da78f6c7b68c2bed0c64ea900649a3b01d13eea6 [file] [edit]
/*
* 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 <filesystem>
#include <iostream>
#include <string>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <config.pb.h>
#include <json/json.h>
#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<ResolvedTask>
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<std::vector<ResolvedProbe>>
resolveProbes(::uprobestats::protos::UprobestatsConfig::Task &taskConfig) {
if (taskConfig.probe_configs().size() == 0) {
LOG(ERROR) << "task has no probe configs";
return {};
}
std::vector<ResolvedProbe> 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<std::string> 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