1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "uprobestats"
18
19 #include <filesystem>
20 #include <iostream>
21 #include <string>
22
23 #include <android-base/file.h>
24 #include <android-base/logging.h>
25 #include <android-base/parseint.h>
26 #include <android-base/properties.h>
27 #include <android-base/strings.h>
28 #include <config.pb.h>
29 #include <json/json.h>
30
31 #include "Art.h"
32 #include "ConfigResolver.h"
33 #include "DebugLog.h"
34 #include "DynamicInstrumentationManager.h"
35 #include "FlagSelector.h"
36 #include "Process.h"
37
38 namespace android {
39 namespace uprobestats {
40 namespace config_resolver {
41
operator <<(std::ostream & os,const ResolvedTask & c)42 std::ostream &operator<<(std::ostream &os, const ResolvedTask &c) {
43 os << "pid: " << c.pid << " taskConfig: " << c.taskConfig.DebugString();
44 return os;
45 }
46
operator <<(std::ostream & os,const ResolvedProbe & c)47 std::ostream &operator<<(std::ostream &os, const ResolvedProbe &c) {
48 os << "filename: " << c.filename << " offset: " << c.offset
49 << " probeConfig: " << c.probeConfig.DebugString();
50 return os;
51 }
52
53 // Reads probing configuration from a file, which should be the serialized
54 // bytes of a UprobestatsConfig proto.
55 std::optional<::uprobestats::protos::UprobestatsConfig>
readConfig(std::string configFilePath)56 readConfig(std::string configFilePath) {
57 std::string config_str;
58 if (!android::base::ReadFileToString(configFilePath, &config_str)) {
59 LOG(ERROR) << "Failed to open config file " << configFilePath;
60 return {};
61 }
62
63 ::uprobestats::protos::UprobestatsConfig config;
64 bool success = config.ParseFromString(config_str);
65 if (!success) {
66 LOG(ERROR) << "Failed to parse file " << configFilePath
67 << " to UprobestatsConfig";
68 return {};
69 }
70
71 return config;
72 }
73
74 std::optional<ResolvedTask>
resolveSingleTask(::uprobestats::protos::UprobestatsConfig config)75 resolveSingleTask(::uprobestats::protos::UprobestatsConfig config) {
76 auto task_count = config.tasks().size();
77 if (task_count == 0) {
78 LOG(ERROR) << "config has no tasks";
79 return {};
80 }
81 if (task_count > 1) {
82 LOG(ERROR) << "config has " << task_count
83 << " tasks. Only 1 is supported. The first task is read and the "
84 "rest are ignored.";
85 }
86 auto taskConfig = config.tasks().Get(0);
87 if (!taskConfig.has_duration_seconds()) {
88 LOG(ERROR) << "config task has no duration";
89 return {};
90 }
91 if (taskConfig.duration_seconds() <= 0) {
92 LOG(ERROR) << "config task cannot have zero or negative duration";
93 }
94 if (!taskConfig.has_target_process_name()) {
95 LOG(ERROR) << "task.target_process_name is required.";
96 return {};
97 }
98 if (taskConfig.target_process_name() != "system_server") {
99 LOG(ERROR)
100 << "system_server is the only target process currently supported";
101 return {};
102 }
103 auto process_name = taskConfig.target_process_name();
104 int pid = process::getPid(process_name);
105 if (pid < 0) {
106 LOG(ERROR) << "Unable to find pid of " << process_name;
107 return {};
108 }
109 ResolvedTask task;
110 task.taskConfig = taskConfig;
111 task.pid = pid;
112 return task;
113 }
114
115 std::optional<std::vector<ResolvedProbe>>
resolveProbes(::uprobestats::protos::UprobestatsConfig::Task & taskConfig)116 resolveProbes(::uprobestats::protos::UprobestatsConfig::Task &taskConfig) {
117 if (taskConfig.probe_configs().size() == 0) {
118 LOG(ERROR) << "task has no probe configs";
119 return {};
120 }
121 std::vector<ResolvedProbe> result;
122 for (auto &probeConfig : taskConfig.probe_configs()) {
123 if (android::uprobestats::flag_selector::executable_method_file_offsets() &&
124 probeConfig.has_fully_qualified_class_name()) {
125 LOG_IF_DEBUG("using getExecutableMethodFileOffsets to retrieve offsets");
126 std::vector<std::string> fqParameters(
127 probeConfig.fully_qualified_parameters().begin(),
128 probeConfig.fully_qualified_parameters().end());
129 std::string processName(taskConfig.target_process_name());
130 std::string fqcn(probeConfig.fully_qualified_class_name());
131 std::string methodName(probeConfig.method_name());
132 std::optional<
133 dynamic_instrumentation_manager::ExecutableMethodFileOffsets>
134 offsets =
135 dynamic_instrumentation_manager::getExecutableMethodFileOffsets(
136 processName, fqcn, methodName, fqParameters);
137 if (!offsets.has_value()) {
138 LOG(ERROR) << "Unable to find method offset for "
139 << probeConfig.fully_qualified_class_name() << "#"
140 << probeConfig.method_name();
141 return {};
142 }
143
144 ResolvedProbe probe;
145 probe.filename = offsets->containerPath;
146 probe.offset = offsets->methodOffset;
147 probe.probeConfig = probeConfig;
148 result.push_back(probe);
149 continue;
150 }
151
152 LOG_IF_DEBUG("using oatdump to retrieve offsets");
153 int offset = 0;
154 std::string matched_file_path;
155 for (auto &file_path : probeConfig.file_paths()) {
156 offset = art::getMethodOffsetFromOatdump(file_path,
157 probeConfig.method_signature());
158 if (offset > 0) {
159 matched_file_path = file_path;
160 break;
161 } else {
162 LOG(WARNING) << "File " << file_path << " has no offset for "
163 << probeConfig.method_signature();
164 }
165 }
166 if (offset == 0) {
167 LOG(ERROR) << "Unable to find method offset for "
168 << probeConfig.method_signature();
169 return {};
170 }
171
172 ResolvedProbe probe;
173 probe.filename = matched_file_path;
174 probe.offset = offset;
175 probe.probeConfig = probeConfig;
176 result.push_back(probe);
177 }
178
179 return result;
180 }
181
182 } // namespace config_resolver
183 } // namespace uprobestats
184 } // namespace android
185