xref: /aosp_15_r20/external/perfetto/src/profiling/common/proc_cmdline.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker /*
2*6dbdd20aSAndroid Build Coastguard Worker  * Copyright (C) 2022 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker  *
4*6dbdd20aSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker  *
8*6dbdd20aSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker  *
10*6dbdd20aSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker  * limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker  */
16*6dbdd20aSAndroid Build Coastguard Worker 
17*6dbdd20aSAndroid Build Coastguard Worker #include "src/profiling/common/proc_cmdline.h"
18*6dbdd20aSAndroid Build Coastguard Worker 
19*6dbdd20aSAndroid Build Coastguard Worker #include <fcntl.h>
20*6dbdd20aSAndroid Build Coastguard Worker #include <fnmatch.h>
21*6dbdd20aSAndroid Build Coastguard Worker #include <string.h>
22*6dbdd20aSAndroid Build Coastguard Worker #include <sys/stat.h>
23*6dbdd20aSAndroid Build Coastguard Worker #include <sys/types.h>
24*6dbdd20aSAndroid Build Coastguard Worker #include <unistd.h>
25*6dbdd20aSAndroid Build Coastguard Worker 
26*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/file_utils.h"
27*6dbdd20aSAndroid Build Coastguard Worker 
28*6dbdd20aSAndroid Build Coastguard Worker namespace perfetto {
29*6dbdd20aSAndroid Build Coastguard Worker namespace profiling {
30*6dbdd20aSAndroid Build Coastguard Worker namespace glob_aware {
31*6dbdd20aSAndroid Build Coastguard Worker 
32*6dbdd20aSAndroid Build Coastguard Worker // Edge cases: the raw cmdline as read out of the kernel can have several
33*6dbdd20aSAndroid Build Coastguard Worker // shapes, the process can rewrite the contents to be arbitrary, and overly long
34*6dbdd20aSAndroid Build Coastguard Worker // cmdlines can be truncated as we use a 511 byte limit. Some examples to
35*6dbdd20aSAndroid Build Coastguard Worker // consider for the implementation:
36*6dbdd20aSAndroid Build Coastguard Worker // * "echo\0hello\0"
37*6dbdd20aSAndroid Build Coastguard Worker // * "/bin/top\0\0\0\0\0\0\0"
38*6dbdd20aSAndroid Build Coastguard Worker // * "arbitrary string as rewritten by the process\0"
39*6dbdd20aSAndroid Build Coastguard Worker // * "some_bugged_kernels_forget_final_nul_terminator"
40*6dbdd20aSAndroid Build Coastguard Worker //
41*6dbdd20aSAndroid Build Coastguard Worker // The approach when performing the read->derive->match is to minimize early
42*6dbdd20aSAndroid Build Coastguard Worker // return codepaths for the caller. So even if we read a non-conforming cmdline
43*6dbdd20aSAndroid Build Coastguard Worker // (e.g. just a single nul byte), it can still be fed through FindBinaryName and
44*6dbdd20aSAndroid Build Coastguard Worker // MatchGlobPattern. It'll just make the intermediate strings be empty (so
45*6dbdd20aSAndroid Build Coastguard Worker // starting with a nul byte, but never nullptr).
46*6dbdd20aSAndroid Build Coastguard Worker //
47*6dbdd20aSAndroid Build Coastguard Worker // NB: bionic/libc/bionic/malloc_heapprofd will require a parallel
48*6dbdd20aSAndroid Build Coastguard Worker // implementation of these functions (to avoid a bionic->perfetto dependency).
49*6dbdd20aSAndroid Build Coastguard Worker // Keep them as STL-free as possible to allow for both implementations to be
50*6dbdd20aSAndroid Build Coastguard Worker // close to verbatim copies.
51*6dbdd20aSAndroid Build Coastguard Worker 
52*6dbdd20aSAndroid Build Coastguard Worker // TODO(rsavitski): consider changing to std::optional<> return type.
ReadProcCmdlineForPID(pid_t pid,std::string * cmdline_out)53*6dbdd20aSAndroid Build Coastguard Worker bool ReadProcCmdlineForPID(pid_t pid, std::string* cmdline_out) {
54*6dbdd20aSAndroid Build Coastguard Worker   std::string filename = "/proc/" + std::to_string(pid) + "/cmdline";
55*6dbdd20aSAndroid Build Coastguard Worker   base::ScopedFile fd(base::OpenFile(filename, O_RDONLY));
56*6dbdd20aSAndroid Build Coastguard Worker   if (!fd) {
57*6dbdd20aSAndroid Build Coastguard Worker     PERFETTO_DPLOG("Failed to open %s", filename.c_str());
58*6dbdd20aSAndroid Build Coastguard Worker     return false;
59*6dbdd20aSAndroid Build Coastguard Worker   }
60*6dbdd20aSAndroid Build Coastguard Worker 
61*6dbdd20aSAndroid Build Coastguard Worker   // buf is 511 bytes to match an implementation that adds a null terminator to
62*6dbdd20aSAndroid Build Coastguard Worker   // the back of a 512 byte buffer.
63*6dbdd20aSAndroid Build Coastguard Worker   char buf[511];
64*6dbdd20aSAndroid Build Coastguard Worker   ssize_t rd = PERFETTO_EINTR(read(*fd, buf, sizeof(buf)));
65*6dbdd20aSAndroid Build Coastguard Worker   if (rd < 0) {
66*6dbdd20aSAndroid Build Coastguard Worker     PERFETTO_DPLOG("Failed to read %s", filename.c_str());
67*6dbdd20aSAndroid Build Coastguard Worker     return false;
68*6dbdd20aSAndroid Build Coastguard Worker   }
69*6dbdd20aSAndroid Build Coastguard Worker 
70*6dbdd20aSAndroid Build Coastguard Worker   cmdline_out->assign(buf, static_cast<size_t>(rd));
71*6dbdd20aSAndroid Build Coastguard Worker   return true;
72*6dbdd20aSAndroid Build Coastguard Worker }
73*6dbdd20aSAndroid Build Coastguard Worker 
74*6dbdd20aSAndroid Build Coastguard Worker // Returns a pointer into |cmdline| corresponding to the argv0 without any
75*6dbdd20aSAndroid Build Coastguard Worker // leading directories if the binary path is absolute. |cmdline_len| corresponds
76*6dbdd20aSAndroid Build Coastguard Worker // to the length of the cmdline string as read out of procfs as a C string -
77*6dbdd20aSAndroid Build Coastguard Worker // length doesn't include the final nul terminator, but it must be present at
78*6dbdd20aSAndroid Build Coastguard Worker // cmdline[cmdline_len]. Note that normally the string itself will contain nul
79*6dbdd20aSAndroid Build Coastguard Worker // bytes, as that's what the kernel uses to separate arguments.
80*6dbdd20aSAndroid Build Coastguard Worker //
81*6dbdd20aSAndroid Build Coastguard Worker // Function output examples:
82*6dbdd20aSAndroid Build Coastguard Worker // * /system/bin/adb\0--flag -> adb
83*6dbdd20aSAndroid Build Coastguard Worker // * adb -> adb
84*6dbdd20aSAndroid Build Coastguard Worker // * com.example.app -> com.example.app
FindBinaryName(const char * cmdline,size_t cmdline_len)85*6dbdd20aSAndroid Build Coastguard Worker const char* FindBinaryName(const char* cmdline, size_t cmdline_len) {
86*6dbdd20aSAndroid Build Coastguard Worker   // Find the first nul byte that signifies the end of argv0. We might not find
87*6dbdd20aSAndroid Build Coastguard Worker   // one if the process rewrote its cmdline without nul separators, and/or the
88*6dbdd20aSAndroid Build Coastguard Worker   // cmdline didn't fully fit into our read buffer. In such cases, proceed with
89*6dbdd20aSAndroid Build Coastguard Worker   // the full string to do best-effort matching.
90*6dbdd20aSAndroid Build Coastguard Worker   const char* argv0_end =
91*6dbdd20aSAndroid Build Coastguard Worker       static_cast<const char*>(memchr(cmdline, '\0', cmdline_len));
92*6dbdd20aSAndroid Build Coastguard Worker   if (argv0_end == nullptr) {
93*6dbdd20aSAndroid Build Coastguard Worker     argv0_end = cmdline + cmdline_len;  // set to final nul terminator
94*6dbdd20aSAndroid Build Coastguard Worker   }
95*6dbdd20aSAndroid Build Coastguard Worker   // Find the last path separator of argv0, if it exists.
96*6dbdd20aSAndroid Build Coastguard Worker   const char* name_start = static_cast<const char*>(
97*6dbdd20aSAndroid Build Coastguard Worker       memrchr(cmdline, '/', static_cast<size_t>(argv0_end - cmdline)));
98*6dbdd20aSAndroid Build Coastguard Worker   if (name_start == nullptr) {
99*6dbdd20aSAndroid Build Coastguard Worker     name_start = cmdline;
100*6dbdd20aSAndroid Build Coastguard Worker   } else {
101*6dbdd20aSAndroid Build Coastguard Worker     name_start++;  // skip the separator
102*6dbdd20aSAndroid Build Coastguard Worker   }
103*6dbdd20aSAndroid Build Coastguard Worker   return name_start;
104*6dbdd20aSAndroid Build Coastguard Worker }
105*6dbdd20aSAndroid Build Coastguard Worker 
106*6dbdd20aSAndroid Build Coastguard Worker // All inputs must be non-nullptr, but can start with a nul byte.
MatchGlobPattern(const char * pattern,const char * cmdline,const char * binname)107*6dbdd20aSAndroid Build Coastguard Worker bool MatchGlobPattern(const char* pattern,
108*6dbdd20aSAndroid Build Coastguard Worker                       const char* cmdline,
109*6dbdd20aSAndroid Build Coastguard Worker                       const char* binname) {
110*6dbdd20aSAndroid Build Coastguard Worker   if (pattern[0] == '/') {
111*6dbdd20aSAndroid Build Coastguard Worker     return fnmatch(pattern, cmdline, FNM_NOESCAPE) == 0;
112*6dbdd20aSAndroid Build Coastguard Worker   }
113*6dbdd20aSAndroid Build Coastguard Worker   return fnmatch(pattern, binname, FNM_NOESCAPE) == 0;
114*6dbdd20aSAndroid Build Coastguard Worker }
115*6dbdd20aSAndroid Build Coastguard Worker 
116*6dbdd20aSAndroid Build Coastguard Worker }  // namespace glob_aware
117*6dbdd20aSAndroid Build Coastguard Worker }  // namespace profiling
118*6dbdd20aSAndroid Build Coastguard Worker }  // namespace perfetto
119