xref: /aosp_15_r20/external/bcc/examples/cpp/pyperf/PyPerfUtil.cc (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1*387f9dfdSAndroid Build Coastguard Worker /*
2*387f9dfdSAndroid Build Coastguard Worker  * Copyright (c) Facebook, Inc.
3*387f9dfdSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License")
4*387f9dfdSAndroid Build Coastguard Worker  */
5*387f9dfdSAndroid Build Coastguard Worker 
6*387f9dfdSAndroid Build Coastguard Worker #include <algorithm>
7*387f9dfdSAndroid Build Coastguard Worker #include <cerrno>
8*387f9dfdSAndroid Build Coastguard Worker #include <chrono>
9*387f9dfdSAndroid Build Coastguard Worker #include <cstdio>
10*387f9dfdSAndroid Build Coastguard Worker #include <cstring>
11*387f9dfdSAndroid Build Coastguard Worker #include <exception>
12*387f9dfdSAndroid Build Coastguard Worker 
13*387f9dfdSAndroid Build Coastguard Worker #include <dirent.h>
14*387f9dfdSAndroid Build Coastguard Worker #include <linux/elf.h>
15*387f9dfdSAndroid Build Coastguard Worker #include <sys/stat.h>
16*387f9dfdSAndroid Build Coastguard Worker #include <sys/types.h>
17*387f9dfdSAndroid Build Coastguard Worker #include <unistd.h>
18*387f9dfdSAndroid Build Coastguard Worker 
19*387f9dfdSAndroid Build Coastguard Worker #include "PyPerfLoggingHelper.h"
20*387f9dfdSAndroid Build Coastguard Worker #include "PyPerfUtil.h"
21*387f9dfdSAndroid Build Coastguard Worker #include "bcc_elf.h"
22*387f9dfdSAndroid Build Coastguard Worker #include "bcc_proc.h"
23*387f9dfdSAndroid Build Coastguard Worker #include "bcc_syms.h"
24*387f9dfdSAndroid Build Coastguard Worker 
25*387f9dfdSAndroid Build Coastguard Worker namespace ebpf {
26*387f9dfdSAndroid Build Coastguard Worker namespace pyperf {
27*387f9dfdSAndroid Build Coastguard Worker 
28*387f9dfdSAndroid Build Coastguard Worker extern OffsetConfig kPy36OffsetConfig;
29*387f9dfdSAndroid Build Coastguard Worker extern std::string PYPERF_BPF_PROGRAM;
30*387f9dfdSAndroid Build Coastguard Worker 
31*387f9dfdSAndroid Build Coastguard Worker const static int kPerfBufSizePages = 32;
32*387f9dfdSAndroid Build Coastguard Worker 
33*387f9dfdSAndroid Build Coastguard Worker const static std::string kPidCfgTableName("pid_config");
34*387f9dfdSAndroid Build Coastguard Worker const static std::string kProgsTableName("progs");
35*387f9dfdSAndroid Build Coastguard Worker const static std::string kSamplePerfBufName("events");
36*387f9dfdSAndroid Build Coastguard Worker 
37*387f9dfdSAndroid Build Coastguard Worker const static std::string kOnEventFuncName("on_event");
38*387f9dfdSAndroid Build Coastguard Worker 
39*387f9dfdSAndroid Build Coastguard Worker const static std::string kPythonStackFuncName("read_python_stack");
40*387f9dfdSAndroid Build Coastguard Worker const static std::string kPythonStackProgIdxFlag("-DPYTHON_STACK_PROG_IDX=");
41*387f9dfdSAndroid Build Coastguard Worker const static int kPythonStackProgIdx = 0;
42*387f9dfdSAndroid Build Coastguard Worker 
43*387f9dfdSAndroid Build Coastguard Worker const static std::string kNumCpusFlag("-DNUM_CPUS=");
44*387f9dfdSAndroid Build Coastguard Worker const static std::string kSymbolsHashSizeFlag("-D__SYMBOLS_SIZE__=");
45*387f9dfdSAndroid Build Coastguard Worker const static int kSymbolsHashSize = 16384;
46*387f9dfdSAndroid Build Coastguard Worker 
47*387f9dfdSAndroid Build Coastguard Worker namespace {
48*387f9dfdSAndroid Build Coastguard Worker 
getRunningPids(std::vector<int> & output)49*387f9dfdSAndroid Build Coastguard Worker bool getRunningPids(std::vector<int>& output) {
50*387f9dfdSAndroid Build Coastguard Worker   auto dir = ::opendir("/proc/");
51*387f9dfdSAndroid Build Coastguard Worker   if (!dir) {
52*387f9dfdSAndroid Build Coastguard Worker     std::fprintf(stderr, "Open /proc failed: %d\n", errno);
53*387f9dfdSAndroid Build Coastguard Worker     return false;
54*387f9dfdSAndroid Build Coastguard Worker   }
55*387f9dfdSAndroid Build Coastguard Worker 
56*387f9dfdSAndroid Build Coastguard Worker   dirent* result = nullptr;
57*387f9dfdSAndroid Build Coastguard Worker   do {
58*387f9dfdSAndroid Build Coastguard Worker     if ((result = readdir(dir))) {
59*387f9dfdSAndroid Build Coastguard Worker       std::string basename = result->d_name;
60*387f9dfdSAndroid Build Coastguard Worker       if (basename == "." || basename == "..") {
61*387f9dfdSAndroid Build Coastguard Worker         continue;
62*387f9dfdSAndroid Build Coastguard Worker       }
63*387f9dfdSAndroid Build Coastguard Worker 
64*387f9dfdSAndroid Build Coastguard Worker       std::string fullpath = "/proc/" + basename;
65*387f9dfdSAndroid Build Coastguard Worker       struct stat st;
66*387f9dfdSAndroid Build Coastguard Worker       if (::stat(fullpath.c_str(), &st) != 0 || !S_ISDIR(st.st_mode)) {
67*387f9dfdSAndroid Build Coastguard Worker         continue;
68*387f9dfdSAndroid Build Coastguard Worker       }
69*387f9dfdSAndroid Build Coastguard Worker 
70*387f9dfdSAndroid Build Coastguard Worker       try {
71*387f9dfdSAndroid Build Coastguard Worker         auto pid = std::stoi(basename);
72*387f9dfdSAndroid Build Coastguard Worker         output.push_back(pid);
73*387f9dfdSAndroid Build Coastguard Worker       } catch (const std::exception& e) {
74*387f9dfdSAndroid Build Coastguard Worker         continue;
75*387f9dfdSAndroid Build Coastguard Worker       }
76*387f9dfdSAndroid Build Coastguard Worker     }
77*387f9dfdSAndroid Build Coastguard Worker   } while (result);
78*387f9dfdSAndroid Build Coastguard Worker 
79*387f9dfdSAndroid Build Coastguard Worker   if (::closedir(dir) == -1) {
80*387f9dfdSAndroid Build Coastguard Worker     std::fprintf(stderr, "Close /proc failed: %d\n", errno);
81*387f9dfdSAndroid Build Coastguard Worker     return false;
82*387f9dfdSAndroid Build Coastguard Worker   }
83*387f9dfdSAndroid Build Coastguard Worker 
84*387f9dfdSAndroid Build Coastguard Worker   return true;
85*387f9dfdSAndroid Build Coastguard Worker }
86*387f9dfdSAndroid Build Coastguard Worker 
87*387f9dfdSAndroid Build Coastguard Worker typedef struct {
88*387f9dfdSAndroid Build Coastguard Worker   int pid;
89*387f9dfdSAndroid Build Coastguard Worker   bool found;
90*387f9dfdSAndroid Build Coastguard Worker   uint64_t st;
91*387f9dfdSAndroid Build Coastguard Worker   uint64_t en;
92*387f9dfdSAndroid Build Coastguard Worker } FindPythonPathHelper;
93*387f9dfdSAndroid Build Coastguard Worker 
94*387f9dfdSAndroid Build Coastguard Worker const static std::string kPy36LibName = "libpython3.6";
95*387f9dfdSAndroid Build Coastguard Worker 
findPythonPathCallback(mod_info * mod,int,void * payload)96*387f9dfdSAndroid Build Coastguard Worker int findPythonPathCallback(mod_info *mod, int, void* payload) {
97*387f9dfdSAndroid Build Coastguard Worker   auto helper = static_cast<FindPythonPathHelper*>(payload);
98*387f9dfdSAndroid Build Coastguard Worker   std::string file = mod->name;
99*387f9dfdSAndroid Build Coastguard Worker   auto pos = file.rfind("/");
100*387f9dfdSAndroid Build Coastguard Worker   if (pos != std::string::npos) {
101*387f9dfdSAndroid Build Coastguard Worker     file = file.substr(pos + 1);
102*387f9dfdSAndroid Build Coastguard Worker   }
103*387f9dfdSAndroid Build Coastguard Worker   if (file.find(kPy36LibName) == 0) {
104*387f9dfdSAndroid Build Coastguard Worker     logInfo(1, "Found Python library %s loaded at %lx-%lx for PID %d\n", mod->name,
105*387f9dfdSAndroid Build Coastguard Worker             mod->start_addr, mod->end_addr, helper->pid);
106*387f9dfdSAndroid Build Coastguard Worker     helper->found = true;
107*387f9dfdSAndroid Build Coastguard Worker     helper->st = mod->start_addr;
108*387f9dfdSAndroid Build Coastguard Worker     helper->en = mod->end_addr;
109*387f9dfdSAndroid Build Coastguard Worker     return -1;
110*387f9dfdSAndroid Build Coastguard Worker   }
111*387f9dfdSAndroid Build Coastguard Worker   return 0;
112*387f9dfdSAndroid Build Coastguard Worker }
113*387f9dfdSAndroid Build Coastguard Worker 
allAddrFound(const PidData & data)114*387f9dfdSAndroid Build Coastguard Worker bool allAddrFound(const PidData& data) {
115*387f9dfdSAndroid Build Coastguard Worker   return (data.current_state_addr > 0) && (data.tls_key_addr > 0) &&
116*387f9dfdSAndroid Build Coastguard Worker          (data.gil_locked_addr > 0) && (data.gil_last_holder_addr > 0);
117*387f9dfdSAndroid Build Coastguard Worker }
118*387f9dfdSAndroid Build Coastguard Worker 
getAddrOfPythonBinaryCallback(const char * name,uint64_t addr,uint64_t,void * payload)119*387f9dfdSAndroid Build Coastguard Worker int getAddrOfPythonBinaryCallback(const char* name, uint64_t addr, uint64_t,
120*387f9dfdSAndroid Build Coastguard Worker                                   void* payload) {
121*387f9dfdSAndroid Build Coastguard Worker   PidData& data = *static_cast<PidData*>(payload);
122*387f9dfdSAndroid Build Coastguard Worker 
123*387f9dfdSAndroid Build Coastguard Worker   auto checkAndGetAddr = [&](uintptr_t& targetAddr, const char* targetName) {
124*387f9dfdSAndroid Build Coastguard Worker     if (targetAddr == 0 && std::strcmp(name, targetName) == 0) {
125*387f9dfdSAndroid Build Coastguard Worker       targetAddr = addr;
126*387f9dfdSAndroid Build Coastguard Worker     }
127*387f9dfdSAndroid Build Coastguard Worker   };
128*387f9dfdSAndroid Build Coastguard Worker 
129*387f9dfdSAndroid Build Coastguard Worker   checkAndGetAddr(data.tls_key_addr, "autoTLSkey");
130*387f9dfdSAndroid Build Coastguard Worker   checkAndGetAddr(data.current_state_addr, "_PyThreadState_Current");
131*387f9dfdSAndroid Build Coastguard Worker   checkAndGetAddr(data.gil_locked_addr, "gil_locked");
132*387f9dfdSAndroid Build Coastguard Worker   checkAndGetAddr(data.gil_last_holder_addr, "gil_last_holder");
133*387f9dfdSAndroid Build Coastguard Worker 
134*387f9dfdSAndroid Build Coastguard Worker   if (allAddrFound(data)) {
135*387f9dfdSAndroid Build Coastguard Worker     return -1;
136*387f9dfdSAndroid Build Coastguard Worker   }
137*387f9dfdSAndroid Build Coastguard Worker   return 0;
138*387f9dfdSAndroid Build Coastguard Worker }
139*387f9dfdSAndroid Build Coastguard Worker 
getAddrOfPythonBinary(const std::string & path,PidData & data)140*387f9dfdSAndroid Build Coastguard Worker bool getAddrOfPythonBinary(const std::string& path, PidData& data) {
141*387f9dfdSAndroid Build Coastguard Worker   std::memset(&data, 0, sizeof(data));
142*387f9dfdSAndroid Build Coastguard Worker 
143*387f9dfdSAndroid Build Coastguard Worker   struct bcc_symbol_option option = {.use_debug_file = 0,
144*387f9dfdSAndroid Build Coastguard Worker                                      .check_debug_file_crc = 0,
145*387f9dfdSAndroid Build Coastguard Worker                                      .lazy_symbolize = 1,
146*387f9dfdSAndroid Build Coastguard Worker                                      .use_symbol_type = (1 << STT_OBJECT)};
147*387f9dfdSAndroid Build Coastguard Worker 
148*387f9dfdSAndroid Build Coastguard Worker   bcc_elf_foreach_sym(path.c_str(), &getAddrOfPythonBinaryCallback, &option,
149*387f9dfdSAndroid Build Coastguard Worker                       &data);
150*387f9dfdSAndroid Build Coastguard Worker 
151*387f9dfdSAndroid Build Coastguard Worker   return allAddrFound(data);
152*387f9dfdSAndroid Build Coastguard Worker }
153*387f9dfdSAndroid Build Coastguard Worker }  // namespace
154*387f9dfdSAndroid Build Coastguard Worker 
handleSampleCallback(void * cb_cookie,void * raw_data,int data_size)155*387f9dfdSAndroid Build Coastguard Worker void handleSampleCallback(void* cb_cookie, void* raw_data, int data_size) {
156*387f9dfdSAndroid Build Coastguard Worker   auto profiler = static_cast<PyPerfUtil*>(cb_cookie);
157*387f9dfdSAndroid Build Coastguard Worker   profiler->handleSample(raw_data, data_size);
158*387f9dfdSAndroid Build Coastguard Worker }
159*387f9dfdSAndroid Build Coastguard Worker 
handleLostSamplesCallback(void * cb_cookie,uint64_t lost_cnt)160*387f9dfdSAndroid Build Coastguard Worker void handleLostSamplesCallback(void* cb_cookie, uint64_t lost_cnt) {
161*387f9dfdSAndroid Build Coastguard Worker   auto profiler = static_cast<PyPerfUtil*>(cb_cookie);
162*387f9dfdSAndroid Build Coastguard Worker   profiler->handleLostSamples(lost_cnt);
163*387f9dfdSAndroid Build Coastguard Worker }
164*387f9dfdSAndroid Build Coastguard Worker 
init()165*387f9dfdSAndroid Build Coastguard Worker PyPerfUtil::PyPerfResult PyPerfUtil::init() {
166*387f9dfdSAndroid Build Coastguard Worker   std::vector<std::string> cflags;
167*387f9dfdSAndroid Build Coastguard Worker   cflags.emplace_back(kNumCpusFlag +
168*387f9dfdSAndroid Build Coastguard Worker                       std::to_string(::sysconf(_SC_NPROCESSORS_ONLN)));
169*387f9dfdSAndroid Build Coastguard Worker   cflags.emplace_back(kSymbolsHashSizeFlag + std::to_string(kSymbolsHashSize));
170*387f9dfdSAndroid Build Coastguard Worker   cflags.emplace_back(kPythonStackProgIdxFlag +
171*387f9dfdSAndroid Build Coastguard Worker                       std::to_string(kPythonStackProgIdx));
172*387f9dfdSAndroid Build Coastguard Worker 
173*387f9dfdSAndroid Build Coastguard Worker   auto initRes = bpf_.init(PYPERF_BPF_PROGRAM, cflags);
174*387f9dfdSAndroid Build Coastguard Worker   if (!initRes.ok()) {
175*387f9dfdSAndroid Build Coastguard Worker     std::fprintf(stderr, "Failed to compiled PyPerf BPF programs: %s\n",
176*387f9dfdSAndroid Build Coastguard Worker                  initRes.msg().c_str());
177*387f9dfdSAndroid Build Coastguard Worker     return PyPerfResult::INIT_FAIL;
178*387f9dfdSAndroid Build Coastguard Worker   }
179*387f9dfdSAndroid Build Coastguard Worker 
180*387f9dfdSAndroid Build Coastguard Worker   int progFd = -1;
181*387f9dfdSAndroid Build Coastguard Worker   auto loadRes =
182*387f9dfdSAndroid Build Coastguard Worker       bpf_.load_func(kPythonStackFuncName, BPF_PROG_TYPE_PERF_EVENT, progFd);
183*387f9dfdSAndroid Build Coastguard Worker   if (!loadRes.ok()) {
184*387f9dfdSAndroid Build Coastguard Worker     std::fprintf(stderr, "Failed to load BPF program %s: %s\n",
185*387f9dfdSAndroid Build Coastguard Worker                  kPythonStackFuncName.c_str(), loadRes.msg().c_str());
186*387f9dfdSAndroid Build Coastguard Worker     return PyPerfResult::INIT_FAIL;
187*387f9dfdSAndroid Build Coastguard Worker   }
188*387f9dfdSAndroid Build Coastguard Worker 
189*387f9dfdSAndroid Build Coastguard Worker   auto progTable = bpf_.get_prog_table(kProgsTableName);
190*387f9dfdSAndroid Build Coastguard Worker   auto updateRes = progTable.update_value(kPythonStackProgIdx, progFd);
191*387f9dfdSAndroid Build Coastguard Worker   if (!updateRes.ok()) {
192*387f9dfdSAndroid Build Coastguard Worker     std::fprintf(stderr,
193*387f9dfdSAndroid Build Coastguard Worker                  "Failed to set BPF program %s FD %d to program table: %s\n",
194*387f9dfdSAndroid Build Coastguard Worker                  kPythonStackFuncName.c_str(), progFd, updateRes.msg().c_str());
195*387f9dfdSAndroid Build Coastguard Worker     return PyPerfResult::INIT_FAIL;
196*387f9dfdSAndroid Build Coastguard Worker   }
197*387f9dfdSAndroid Build Coastguard Worker 
198*387f9dfdSAndroid Build Coastguard Worker   std::vector<int> pids;
199*387f9dfdSAndroid Build Coastguard Worker   if (!getRunningPids(pids)) {
200*387f9dfdSAndroid Build Coastguard Worker     std::fprintf(stderr, "Failed getting running Processes\n");
201*387f9dfdSAndroid Build Coastguard Worker     return PyPerfResult::INIT_FAIL;
202*387f9dfdSAndroid Build Coastguard Worker   }
203*387f9dfdSAndroid Build Coastguard Worker 
204*387f9dfdSAndroid Build Coastguard Worker   // Populate config for each Python Process
205*387f9dfdSAndroid Build Coastguard Worker   auto pid_hash = bpf_.get_hash_table<int, PidData>(kPidCfgTableName);
206*387f9dfdSAndroid Build Coastguard Worker   PidData pidData;
207*387f9dfdSAndroid Build Coastguard Worker   for (const auto pid : pids) {
208*387f9dfdSAndroid Build Coastguard Worker     if (!tryTargetPid(pid, pidData)) {
209*387f9dfdSAndroid Build Coastguard Worker       // Not a Python Process
210*387f9dfdSAndroid Build Coastguard Worker       continue;
211*387f9dfdSAndroid Build Coastguard Worker     }
212*387f9dfdSAndroid Build Coastguard Worker     pid_hash.update_value(pid, pidData);
213*387f9dfdSAndroid Build Coastguard Worker   }
214*387f9dfdSAndroid Build Coastguard Worker 
215*387f9dfdSAndroid Build Coastguard Worker   // Open perf buffer
216*387f9dfdSAndroid Build Coastguard Worker   auto openRes = bpf_.open_perf_buffer(
217*387f9dfdSAndroid Build Coastguard Worker       kSamplePerfBufName, &handleSampleCallback, &handleLostSamplesCallback,
218*387f9dfdSAndroid Build Coastguard Worker       this, kPerfBufSizePages);
219*387f9dfdSAndroid Build Coastguard Worker   if (!openRes.ok()) {
220*387f9dfdSAndroid Build Coastguard Worker     std::fprintf(stderr, "Unable to open Perf Buffer: %s\n",
221*387f9dfdSAndroid Build Coastguard Worker                  openRes.msg().c_str());
222*387f9dfdSAndroid Build Coastguard Worker     return PyPerfResult::PERF_BUF_OPEN_FAIL;
223*387f9dfdSAndroid Build Coastguard Worker   }
224*387f9dfdSAndroid Build Coastguard Worker 
225*387f9dfdSAndroid Build Coastguard Worker   initCompleted_ = true;
226*387f9dfdSAndroid Build Coastguard Worker   return PyPerfResult::SUCCESS;
227*387f9dfdSAndroid Build Coastguard Worker }
228*387f9dfdSAndroid Build Coastguard Worker 
handleSample(const void * data,int dataSize)229*387f9dfdSAndroid Build Coastguard Worker void PyPerfUtil::handleSample(const void* data, int dataSize) {
230*387f9dfdSAndroid Build Coastguard Worker   const Event* raw = static_cast<const Event*>(data);
231*387f9dfdSAndroid Build Coastguard Worker   samples_.emplace_back(raw, dataSize);
232*387f9dfdSAndroid Build Coastguard Worker   totalSamples_++;
233*387f9dfdSAndroid Build Coastguard Worker }
234*387f9dfdSAndroid Build Coastguard Worker 
handleLostSamples(int lostCnt)235*387f9dfdSAndroid Build Coastguard Worker void PyPerfUtil::handleLostSamples(int lostCnt) { lostSamples_ += lostCnt; }
236*387f9dfdSAndroid Build Coastguard Worker 
profile(int64_t sampleRate,int64_t durationMs,PyPerfSampleProcessor * processor)237*387f9dfdSAndroid Build Coastguard Worker PyPerfUtil::PyPerfResult PyPerfUtil::profile(int64_t sampleRate,
238*387f9dfdSAndroid Build Coastguard Worker                                              int64_t durationMs,
239*387f9dfdSAndroid Build Coastguard Worker                                              PyPerfSampleProcessor* processor) {
240*387f9dfdSAndroid Build Coastguard Worker   if (!initCompleted_) {
241*387f9dfdSAndroid Build Coastguard Worker     std::fprintf(stderr, "PyPerfUtil::init not invoked or failed\n");
242*387f9dfdSAndroid Build Coastguard Worker     return PyPerfResult::NO_INIT;
243*387f9dfdSAndroid Build Coastguard Worker   }
244*387f9dfdSAndroid Build Coastguard Worker 
245*387f9dfdSAndroid Build Coastguard Worker   // Attach to CPU cycles
246*387f9dfdSAndroid Build Coastguard Worker   auto attachRes =
247*387f9dfdSAndroid Build Coastguard Worker       bpf_.attach_perf_event(0, 0, kOnEventFuncName, sampleRate, 0);
248*387f9dfdSAndroid Build Coastguard Worker   if (!attachRes.ok()) {
249*387f9dfdSAndroid Build Coastguard Worker     std::fprintf(stderr, "Attach to CPU cycles event failed: %s\n",
250*387f9dfdSAndroid Build Coastguard Worker                  attachRes.msg().c_str());
251*387f9dfdSAndroid Build Coastguard Worker     return PyPerfResult::EVENT_ATTACH_FAIL;
252*387f9dfdSAndroid Build Coastguard Worker   }
253*387f9dfdSAndroid Build Coastguard Worker   logInfo(2, "Attached to profiling event\n");
254*387f9dfdSAndroid Build Coastguard Worker 
255*387f9dfdSAndroid Build Coastguard Worker   // Get Perf Buffer and poll in a loop for a given duration
256*387f9dfdSAndroid Build Coastguard Worker   auto perfBuffer = bpf_.get_perf_buffer(kSamplePerfBufName);
257*387f9dfdSAndroid Build Coastguard Worker   if (!perfBuffer) {
258*387f9dfdSAndroid Build Coastguard Worker     std::fprintf(stderr, "Failed to get Perf Buffer: %s\n",
259*387f9dfdSAndroid Build Coastguard Worker                  kSamplePerfBufName.c_str());
260*387f9dfdSAndroid Build Coastguard Worker     return PyPerfResult::PERF_BUF_OPEN_FAIL;
261*387f9dfdSAndroid Build Coastguard Worker   }
262*387f9dfdSAndroid Build Coastguard Worker   logInfo(2, "Started polling Perf Buffer\n");
263*387f9dfdSAndroid Build Coastguard Worker   auto start = std::chrono::steady_clock::now();
264*387f9dfdSAndroid Build Coastguard Worker   while (std::chrono::steady_clock::now() <
265*387f9dfdSAndroid Build Coastguard Worker          start + std::chrono::milliseconds(durationMs)) {
266*387f9dfdSAndroid Build Coastguard Worker     perfBuffer->poll(50 /* 50ms timeout */);
267*387f9dfdSAndroid Build Coastguard Worker   }
268*387f9dfdSAndroid Build Coastguard Worker   logInfo(2, "Profiling duration finished\n");
269*387f9dfdSAndroid Build Coastguard Worker 
270*387f9dfdSAndroid Build Coastguard Worker   // Detach the event
271*387f9dfdSAndroid Build Coastguard Worker   auto detachRes = bpf_.detach_perf_event(0, 0);
272*387f9dfdSAndroid Build Coastguard Worker   if (!detachRes.ok()) {
273*387f9dfdSAndroid Build Coastguard Worker     std::fprintf(stderr, "Detach CPU cycles event failed: %s\n",
274*387f9dfdSAndroid Build Coastguard Worker                  detachRes.msg().c_str());
275*387f9dfdSAndroid Build Coastguard Worker     return PyPerfResult::EVENT_DETACH_FAIL;
276*387f9dfdSAndroid Build Coastguard Worker   }
277*387f9dfdSAndroid Build Coastguard Worker   logInfo(2, "Detached from profiling event\n");
278*387f9dfdSAndroid Build Coastguard Worker 
279*387f9dfdSAndroid Build Coastguard Worker   // Drain remaining samples
280*387f9dfdSAndroid Build Coastguard Worker   logInfo(2, "Draining remaining samples\n");
281*387f9dfdSAndroid Build Coastguard Worker   while (perfBuffer->poll(0) > 0) {
282*387f9dfdSAndroid Build Coastguard Worker   }
283*387f9dfdSAndroid Build Coastguard Worker   logInfo(2, "Finished draining remaining samples\n");
284*387f9dfdSAndroid Build Coastguard Worker 
285*387f9dfdSAndroid Build Coastguard Worker   processor->processSamples(samples_, this);
286*387f9dfdSAndroid Build Coastguard Worker 
287*387f9dfdSAndroid Build Coastguard Worker   return PyPerfResult::SUCCESS;
288*387f9dfdSAndroid Build Coastguard Worker }
289*387f9dfdSAndroid Build Coastguard Worker 
getSymbolMapping()290*387f9dfdSAndroid Build Coastguard Worker std::unordered_map<int32_t, std::string> PyPerfUtil::getSymbolMapping() {
291*387f9dfdSAndroid Build Coastguard Worker   auto symbolTable = bpf_.get_hash_table<Symbol, int32_t>("symbols");
292*387f9dfdSAndroid Build Coastguard Worker   std::unordered_map<int32_t, std::string> symbols;
293*387f9dfdSAndroid Build Coastguard Worker   for (auto& x : symbolTable.get_table_offline()) {
294*387f9dfdSAndroid Build Coastguard Worker     auto symbolName = getSymbolName(x.first);
295*387f9dfdSAndroid Build Coastguard Worker     logInfo(2, "Symbol ID %d is %s\n", x.second, symbolName.c_str());
296*387f9dfdSAndroid Build Coastguard Worker     symbols.emplace(x.second, std::move(symbolName));
297*387f9dfdSAndroid Build Coastguard Worker   }
298*387f9dfdSAndroid Build Coastguard Worker   logInfo(1, "Total %d unique Python symbols\n", symbols.size());
299*387f9dfdSAndroid Build Coastguard Worker   return symbols;
300*387f9dfdSAndroid Build Coastguard Worker }
301*387f9dfdSAndroid Build Coastguard Worker 
getSymbolName(Symbol & sym) const302*387f9dfdSAndroid Build Coastguard Worker std::string PyPerfUtil::getSymbolName(Symbol& sym) const {
303*387f9dfdSAndroid Build Coastguard Worker   std::string nameStr = std::string(sym.name).substr(0, FUNCTION_NAME_LEN);
304*387f9dfdSAndroid Build Coastguard Worker   std::string classStr = std::string(sym.classname).substr(0, CLASS_NAME_LEN);
305*387f9dfdSAndroid Build Coastguard Worker   if (classStr.size() > 0) {
306*387f9dfdSAndroid Build Coastguard Worker     nameStr = classStr + "." + nameStr;
307*387f9dfdSAndroid Build Coastguard Worker   }
308*387f9dfdSAndroid Build Coastguard Worker 
309*387f9dfdSAndroid Build Coastguard Worker   std::string file = std::string(sym.file).substr(0, FILE_NAME_LEN);
310*387f9dfdSAndroid Build Coastguard Worker   if (file.empty()) {
311*387f9dfdSAndroid Build Coastguard Worker     return nameStr;
312*387f9dfdSAndroid Build Coastguard Worker   }
313*387f9dfdSAndroid Build Coastguard Worker   if (file[0] == '/') {
314*387f9dfdSAndroid Build Coastguard Worker     file = file.substr(1);
315*387f9dfdSAndroid Build Coastguard Worker   }
316*387f9dfdSAndroid Build Coastguard Worker   if (file.find("./") == 0) {
317*387f9dfdSAndroid Build Coastguard Worker     file = file.substr(2);
318*387f9dfdSAndroid Build Coastguard Worker   }
319*387f9dfdSAndroid Build Coastguard Worker   if (file.find(".py", file.size() - 3) == (file.size() - 3)) {
320*387f9dfdSAndroid Build Coastguard Worker     file = file.substr(0, file.size() - 3);
321*387f9dfdSAndroid Build Coastguard Worker   }
322*387f9dfdSAndroid Build Coastguard Worker   std::replace(file.begin(), file.end(), '/', '.');
323*387f9dfdSAndroid Build Coastguard Worker 
324*387f9dfdSAndroid Build Coastguard Worker   return file + "." + nameStr;
325*387f9dfdSAndroid Build Coastguard Worker }
326*387f9dfdSAndroid Build Coastguard Worker 
tryTargetPid(int pid,PidData & data)327*387f9dfdSAndroid Build Coastguard Worker bool PyPerfUtil::tryTargetPid(int pid, PidData& data) {
328*387f9dfdSAndroid Build Coastguard Worker   FindPythonPathHelper helper{pid, false, 0, 0};
329*387f9dfdSAndroid Build Coastguard Worker   bcc_procutils_each_module(pid, &findPythonPathCallback, &helper);
330*387f9dfdSAndroid Build Coastguard Worker   if (!helper.found) {
331*387f9dfdSAndroid Build Coastguard Worker     logInfo(2, "PID %d does not contain Python library\n", pid);
332*387f9dfdSAndroid Build Coastguard Worker     return false;
333*387f9dfdSAndroid Build Coastguard Worker   }
334*387f9dfdSAndroid Build Coastguard Worker 
335*387f9dfdSAndroid Build Coastguard Worker   char path[256];
336*387f9dfdSAndroid Build Coastguard Worker   int res = std::snprintf(path, sizeof(path), "/proc/%d/map_files/%lx-%lx", pid,
337*387f9dfdSAndroid Build Coastguard Worker                           helper.st, helper.en);
338*387f9dfdSAndroid Build Coastguard Worker   if (res < 0 || size_t(res) >= sizeof(path)) {
339*387f9dfdSAndroid Build Coastguard Worker     return false;
340*387f9dfdSAndroid Build Coastguard Worker   }
341*387f9dfdSAndroid Build Coastguard Worker 
342*387f9dfdSAndroid Build Coastguard Worker   if (!getAddrOfPythonBinary(path, data)) {
343*387f9dfdSAndroid Build Coastguard Worker     std::fprintf(
344*387f9dfdSAndroid Build Coastguard Worker         stderr,
345*387f9dfdSAndroid Build Coastguard Worker         "Failed getting addresses in potential Python library in PID %d\n",
346*387f9dfdSAndroid Build Coastguard Worker         pid);
347*387f9dfdSAndroid Build Coastguard Worker     return false;
348*387f9dfdSAndroid Build Coastguard Worker   }
349*387f9dfdSAndroid Build Coastguard Worker   data.offsets = kPy36OffsetConfig;
350*387f9dfdSAndroid Build Coastguard Worker   data.current_state_addr += helper.st;
351*387f9dfdSAndroid Build Coastguard Worker   logInfo(2, "PID %d has _PyThreadState_Current at %lx\n", pid,
352*387f9dfdSAndroid Build Coastguard Worker           data.current_state_addr);
353*387f9dfdSAndroid Build Coastguard Worker   data.tls_key_addr += helper.st;
354*387f9dfdSAndroid Build Coastguard Worker   logInfo(2, "PID %d has autoTLSKey at %lx\n", pid, data.current_state_addr);
355*387f9dfdSAndroid Build Coastguard Worker   data.gil_locked_addr += helper.st;
356*387f9dfdSAndroid Build Coastguard Worker   logInfo(2, "PID %d has gil_locked at %lx\n", pid, data.current_state_addr);
357*387f9dfdSAndroid Build Coastguard Worker   data.gil_last_holder_addr += helper.st;
358*387f9dfdSAndroid Build Coastguard Worker   logInfo(2, "PID %d has gil_last_holder at %lx\n", pid,
359*387f9dfdSAndroid Build Coastguard Worker           data.current_state_addr);
360*387f9dfdSAndroid Build Coastguard Worker 
361*387f9dfdSAndroid Build Coastguard Worker   return true;
362*387f9dfdSAndroid Build Coastguard Worker }
363*387f9dfdSAndroid Build Coastguard Worker 
364*387f9dfdSAndroid Build Coastguard Worker }  // namespace pyperf
365*387f9dfdSAndroid Build Coastguard Worker }  // namespace ebpf
366