• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 #include <procinfo/process.h>
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdarg.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include <memory>
28 #include <string>
29 #include <utility>
30 
31 #include <android-base/file.h>
32 #include <android-base/stringprintf.h>
33 #include <android-base/unique_fd.h>
34 
35 using android::base::unique_fd;
36 
37 namespace android {
38 namespace procinfo {
39 
SetError(std::string * error,int errno_value,const char * fmt,...)40 bool SetError(std::string* error, int errno_value, const char* fmt, ...) {
41   if (error == nullptr) return false;
42 
43   std::string result;
44   va_list ap;
45   va_start(ap, fmt);
46   android::base::StringAppendV(&result, fmt, ap);
47   va_end(ap);
48 
49   if (errno_value != 0) {
50     result += ": ";
51     result += strerror(errno_value);
52   }
53 
54   *error = result;
55   return false;
56 }
57 
GetProcessInfo(pid_t tid,ProcessInfo * process_info,std::string * error)58 bool GetProcessInfo(pid_t tid, ProcessInfo* process_info, std::string* error) {
59   char path[32];
60   snprintf(path, sizeof(path), "/proc/%d", tid);
61 
62   unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
63   if (dirfd == -1) {
64     return SetError(error, errno, "failed to open %s", path);
65   }
66 
67   return GetProcessInfoFromProcPidFd(dirfd.get(), tid, process_info, error);
68 }
69 
parse_state(char state)70 static ProcessState parse_state(char state) {
71   switch (state) {
72     case 'R':
73       return kProcessStateRunning;
74     case 'S':
75       return kProcessStateSleeping;
76     case 'D':
77       return kProcessStateUninterruptibleWait;
78     case 'T':
79       return kProcessStateStopped;
80     case 'Z':
81       return kProcessStateZombie;
82     default:
83       return kProcessStateUnknown;
84   }
85 }
86 
GetProcessInfoFromProcPidFd(int fd,int pid,ProcessInfo * process_info,std::string * error)87 bool GetProcessInfoFromProcPidFd(int fd, int pid, ProcessInfo* process_info,
88                                  std::string* error /* can be nullptr */) {
89   int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
90   if (status_fd == -1) {
91     return SetError(error, errno,
92                     "failed to open /proc/%d/status in GetProcessInfoFromProcPidFd", pid);
93   }
94 
95   std::unique_ptr<FILE, decltype(&fclose)> fp(fdopen(status_fd, "re"), fclose);
96   if (!fp) {
97     close(status_fd);
98     return SetError(error, errno, "failed to open FILE for /proc/%d/status in GetProcessInfoFromProcPidFd", pid);
99   }
100 
101   int field_bitmap = 0;
102   static constexpr int finished_bitmap = 63;
103   char* line = nullptr;
104   size_t len = 0;
105 
106   while (getline(&line, &len, fp.get()) != -1 && field_bitmap != finished_bitmap) {
107     char* tab = strchr(line, '\t');
108     if (tab == nullptr) {
109       continue;
110     }
111 
112     size_t header_len = tab - line;
113     std::string header = std::string(line, header_len);
114     if (header == "Name:") {
115       std::string name = line + header_len + 1;
116 
117       // line includes the trailing newline.
118       name.pop_back();
119       process_info->name = std::move(name);
120 
121       field_bitmap |= 1;
122     }  else if (header == "Tgid:") {
123       process_info->pid = atoi(tab + 1);
124       field_bitmap |= 2;
125     } else if (header == "Pid:") {
126       process_info->tid = atoi(tab + 1);
127       field_bitmap |= 4;
128     } else if (header == "TracerPid:") {
129       process_info->tracer = atoi(tab + 1);
130       field_bitmap |= 8;
131     } else if (header == "Uid:") {
132       process_info->uid = atoi(tab + 1);
133       field_bitmap |= 16;
134     } else if (header == "Gid:") {
135       process_info->gid = atoi(tab + 1);
136       field_bitmap |= 32;
137     }
138   }
139 
140   free(line);
141   if (field_bitmap != finished_bitmap) {
142     return SetError(error, 0, "failed to parse /proc/%d/status", pid);
143   }
144 
145   unique_fd stat_fd(openat(fd, "stat", O_RDONLY | O_CLOEXEC));
146   if (stat_fd == -1) {
147     return SetError(error, errno, "failed to open /proc/%d/stat", pid);
148   }
149 
150   std::string stat;
151   if (!android::base::ReadFdToString(stat_fd, &stat)) {
152     return SetError(error, errno, "failed to read /proc/%d/stat", pid);
153   }
154 
155   // See man 5 proc. There's no reason comm can't contain ' ' or ')',
156   // so we search backwards for the end of it.
157   const char* end_of_comm = strrchr(stat.c_str(), ')');
158 
159   static constexpr const char* pattern =
160       "%c "    // state
161       "%d "    // ppid
162       "%*d "   // pgrp
163       "%*d "   // session
164       "%*d "   // tty_nr
165       "%*d "   // tpgid
166       "%*u "   // flags
167       "%*lu "  // minflt
168       "%*lu "  // cminflt
169       "%*lu "  // majflt
170       "%*lu "  // cmajflt
171       "%*lu "  // utime
172       "%*lu "  // stime
173       "%*ld "  // cutime
174       "%*ld "  // cstime
175       "%*ld "  // priority
176       "%*ld "  // nice
177       "%*ld "  // num_threads
178       "%*ld "  // itrealvalue
179       "%llu "  // starttime
180       ;
181 
182   char state = '\0';
183   int ppid = 0;
184   unsigned long long start_time = 0;
185   int rc = sscanf(end_of_comm + 2, pattern, &state, &ppid, &start_time);
186   if (rc != 3) {
187     return SetError(error, 0, "failed to parse /proc/%d/stat", pid);
188   }
189 
190   process_info->state = parse_state(state);
191   process_info->ppid = ppid;
192   process_info->starttime = start_time;
193   return true;
194 }
195 
196 } /* namespace procinfo */
197 } /* namespace android */
198