1 /*
2 * Copyright (C) 2023 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 <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <android-base/parseint.h>
22 #include <android-base/properties.h>
23 #include <android-base/strings.h>
24 #include <android_uprobestats_flags.h>
25 #include <config.pb.h>
26 #include <iostream>
27 #include <stdio.h>
28 #include <string>
29 #include <thread>
30
31 #include "Bpf.h"
32 #include "ConfigResolver.h"
33
34 using namespace android::uprobestats;
35
isUserBuild()36 bool isUserBuild() {
37 return android::base::GetProperty("ro.build.type", "unknown") == "user";
38 }
39
isUprobestatsEnabled()40 bool isUprobestatsEnabled() {
41 return android::uprobestats::flags::enable_uprobestats();
42 }
43
44 const std::string bpf_path = std::string("/sys/fs/bpf/uprobestats/");
prefix_bpf(std::string value)45 std::string prefix_bpf(std::string value) { return bpf_path + value.c_str(); }
46
47 struct PollArgs {
48 std::string map_path;
49 int duration_seconds;
50 };
51
doPoll(PollArgs args)52 void doPoll(PollArgs args) {
53 auto map_path = args.map_path;
54 auto duration_seconds = args.duration_seconds;
55 auto duration = std::chrono::seconds(duration_seconds);
56 auto start_time = std::chrono::steady_clock::now();
57 auto now = start_time;
58 while (now - start_time < duration) {
59 auto remaining = duration - (std::chrono::steady_clock::now() - start_time);
60 auto timeout_ms = static_cast<int>(
61 std::chrono::duration_cast<std::chrono::milliseconds>(remaining)
62 .count());
63 auto result = bpf::pollRingBuf(map_path.c_str(), timeout_ms);
64 for (auto value : result) {
65 LOG(INFO) << "ringbuf result callback. value: " << value
66 << " map_path: " << map_path;
67 }
68 now = std::chrono::steady_clock::now();
69 }
70 LOG(INFO) << "finished polling for map_path: " << map_path;
71 }
72
main(int argc,char ** argv)73 int main(int argc, char **argv) {
74 if (isUserBuild()) {
75 // TODO(296108553): See if we could avoid shipping this binary on user
76 // builds.
77 LOG(ERROR) << "uprobestats disabled on user build. Exiting.";
78 return 1;
79 }
80 if (!isUprobestatsEnabled()) {
81 LOG(ERROR) << "uprobestats disabled by flag. Exiting.";
82 return 1;
83 }
84 if (argc < 2) {
85 LOG(ERROR) << "Not enough command line arguments. Exiting.";
86 return 1;
87 }
88
89 auto config = config_resolver::readConfig(
90 std::string("/data/misc/uprobestats-configs/") + argv[1]);
91 if (!config.has_value()) {
92 return 1;
93 }
94 auto resolved_task = config_resolver::resolveSingleTask(config.value());
95 if (!resolved_task.has_value()) {
96 return 1;
97 }
98
99 LOG(INFO) << "Found task config: " << resolved_task.value();
100 std::set<std::string> map_paths;
101 auto resolved_probe_configs =
102 config_resolver::resolveProbes(resolved_task.value().task_config);
103 if (!resolved_probe_configs.has_value()) {
104 return 1;
105 }
106 for (auto &resolved_probe : resolved_probe_configs.value()) {
107 LOG(INFO) << "Opening bpf perf event from probe: " << resolved_probe;
108 map_paths.insert(prefix_bpf(resolved_probe.probe_config.bpf_map()));
109 bpf::bpfPerfEventOpen(
110 resolved_probe.filename.c_str(), resolved_probe.offset,
111 resolved_task.value().pid,
112 prefix_bpf(resolved_probe.probe_config.bpf_name()).c_str());
113 }
114
115 std::vector<std::thread> threads;
116 for (auto map_path : map_paths) {
117 auto poll_args = PollArgs{
118 map_path, resolved_task.value().task_config.duration_seconds()};
119 LOG(INFO) << "Starting thread to collect results from map_path: "
120 << map_path;
121 threads.emplace_back(doPoll, poll_args);
122 }
123 for (auto &thread : threads) {
124 thread.join();
125 }
126
127 LOG(INFO) << "done.";
128
129 return 0;
130 }
131