• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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