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