OSDN Git Service

ANRdaemon: move trace result from /sdcard to /data
[android-x86/system-extras.git] / taskstats / taskstats.c
1 /*
2  * Copyright (C) 2013 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 /*
18  * Linux task stats reporting tool. Queries and prints out the kernel's
19  * taskstats structure for a given process or thread group id. See
20  * https://www.kernel.org/doc/Documentation/accounting/ for more information
21  * about the reported fields.
22  */
23
24 #include <errno.h>
25 #include <getopt.h>
26 #include <netlink/attr.h>
27 #include <netlink/genl/genl.h>
28 #include <netlink/handlers.h>
29 #include <netlink/msg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <sys/cdefs.h>
33 #include <time.h>
34 #include <unistd.h>
35
36 #include <linux/taskstats.h>
37
38 struct TaskStatistics {
39     int pid;
40     int tgid;
41     struct taskstats stats;
42 };
43
44 int send_command(struct nl_sock* netlink_socket, uint16_t nlmsg_type,
45                  uint32_t nlmsg_pid, uint8_t genl_cmd, uint16_t nla_type,
46                  void* nla_data, int nla_len) {
47     struct nl_msg* message = nlmsg_alloc();
48     int seq = 0;
49     int version = 1;
50     int header_length = 0;
51     int flags = NLM_F_REQUEST;
52     genlmsg_put(message, nlmsg_pid, seq, nlmsg_type, header_length, flags,
53                 genl_cmd, version);
54     nla_put(message, nla_type, nla_len, nla_data);
55
56     /* Override the header flags since we don't want NLM_F_ACK. */
57     struct nlmsghdr* header = nlmsg_hdr(message);
58     header->nlmsg_flags = flags;
59
60     int result = nl_send(netlink_socket, message);
61     nlmsg_free(message);
62     return result;
63 }
64
65 int print_receive_error(struct sockaddr_nl* address __unused,
66                         struct nlmsgerr* error, void* arg __unused) {
67     fprintf(stderr, "Netlink receive error: %s\n", strerror(-error->error));
68     return NL_STOP;
69 }
70
71 int parse_family_id(struct nl_msg* msg, void* arg) {
72     struct genlmsghdr* gnlh = (struct genlmsghdr*)nlmsg_data(nlmsg_hdr(msg));
73     struct nlattr* attr = genlmsg_attrdata(gnlh, 0);
74     int remaining = genlmsg_attrlen(gnlh, 0);
75
76     do {
77         if (attr->nla_type == CTRL_ATTR_FAMILY_ID) {
78             *((int*)arg) = nla_get_u16(attr);
79             return NL_STOP;
80         }
81     } while ((attr = nla_next(attr, &remaining)));
82     return NL_OK;
83 }
84
85 int get_family_id(struct nl_sock* netlink_socket, const char* name) {
86     if (send_command(netlink_socket, GENL_ID_CTRL, getpid(),
87                      CTRL_CMD_GETFAMILY,
88                      CTRL_ATTR_FAMILY_NAME,
89                      (void*)name, strlen(name) + 1) < 0) {
90         return 0;
91     }
92
93     int family_id = 0;
94     struct nl_cb* callbacks = nl_cb_get(nl_cb_alloc(NL_CB_VALID));
95     nl_cb_set(callbacks, NL_CB_VALID, NL_CB_DEFAULT, &parse_family_id,
96               &family_id);
97     nl_cb_err(callbacks, NL_CB_DEFAULT, &print_receive_error, NULL);
98
99     if (nl_recvmsgs(netlink_socket, callbacks) < 0) {
100         return 0;
101     }
102     nl_cb_put(callbacks);
103     return family_id;
104 }
105
106 void parse_aggregate_task_stats(struct nlattr* attr, int attr_size,
107                                 struct TaskStatistics* stats) {
108     do {
109         switch (attr->nla_type) {
110             case TASKSTATS_TYPE_PID:
111                 stats->pid = nla_get_u32(attr);
112                 break;
113             case TASKSTATS_TYPE_TGID:
114                 stats->tgid = nla_get_u32(attr);
115                 break;
116             case TASKSTATS_TYPE_STATS:
117                 nla_memcpy(&stats->stats, attr, sizeof(stats->stats));
118                 break;
119             default:
120                 break;
121         }
122     } while ((attr = nla_next(attr, &attr_size)));
123 }
124
125 int parse_task_stats(struct nl_msg* msg, void* arg) {
126     struct TaskStatistics* stats = (struct TaskStatistics*)arg;
127     struct genlmsghdr* gnlh = (struct genlmsghdr*)nlmsg_data(nlmsg_hdr(msg));
128     struct nlattr* attr = genlmsg_attrdata(gnlh, 0);
129     int remaining = genlmsg_attrlen(gnlh, 0);
130
131     do {
132         switch (attr->nla_type) {
133             case TASKSTATS_TYPE_AGGR_PID:
134             case TASKSTATS_TYPE_AGGR_TGID:
135                 parse_aggregate_task_stats(nla_data(attr), nla_len(attr),
136                                            stats);
137                 break;
138             default:
139                 break;
140         }
141     } while ((attr = nla_next(attr, &remaining)));
142     return NL_STOP;
143 }
144
145 int query_task_stats(struct nl_sock* netlink_socket, int family_id,
146                      int command_type, int parameter,
147                      struct TaskStatistics* stats) {
148     memset(stats, 0, sizeof(*stats));
149     int result = send_command(netlink_socket, family_id, getpid(),
150                               TASKSTATS_CMD_GET, command_type, &parameter,
151                               sizeof(parameter));
152     if (result < 0) {
153         return result;
154     }
155
156     struct nl_cb* callbacks = nl_cb_get(nl_cb_alloc(NL_CB_VALID));
157     nl_cb_set(callbacks, NL_CB_VALID, NL_CB_DEFAULT, &parse_task_stats, stats);
158     nl_cb_err(callbacks, NL_CB_DEFAULT, &print_receive_error, &family_id);
159
160     result = nl_recvmsgs(netlink_socket, callbacks);
161     if (result < 0) {
162         return result;
163     }
164     nl_cb_put(callbacks);
165     return stats->pid || stats->tgid;
166 }
167
168 double average_ms(unsigned long long total, unsigned long long count) {
169     if (!count) {
170         return 0;
171     }
172     return ((double)total) / count / 1e6;
173 }
174
175 unsigned long long average_ns(unsigned long long total,
176                               unsigned long long count) {
177     if (!count) {
178         return 0;
179     }
180     return total / count;
181 }
182
183 void print_task_stats(const struct TaskStatistics* stats,
184                       int human_readable) {
185     const struct taskstats* s = &stats->stats;
186     printf("Basic task statistics\n");
187     printf("---------------------\n");
188     printf("%-25s%d\n", "Stats version:", s->version);
189     printf("%-25s%d\n", "Exit code:", s->ac_exitcode);
190     printf("%-25s0x%x\n", "Flags:", s->ac_flag);
191     printf("%-25s%d\n", "Nice value:", s->ac_nice);
192     printf("%-25s%s\n", "Command name:", s->ac_comm);
193     printf("%-25s%d\n", "Scheduling discipline:", s->ac_sched);
194     printf("%-25s%d\n", "UID:", s->ac_uid);
195     printf("%-25s%d\n", "GID:", s->ac_gid);
196     printf("%-25s%d\n", "PID:", s->ac_pid);
197     printf("%-25s%d\n", "PPID:", s->ac_ppid);
198
199     if (human_readable) {
200         time_t begin_time = s->ac_btime;
201         printf("%-25s%s", "Begin time:", ctime(&begin_time));
202     } else {
203         printf("%-25s%d sec\n", "Begin time:", s->ac_btime);
204     }
205     printf("%-25s%llu usec\n", "Elapsed time:", s->ac_etime);
206     printf("%-25s%llu usec\n", "User CPU time:", s->ac_utime);
207     printf("%-25s%llu\n", "Minor page faults:", s->ac_minflt);
208     printf("%-25s%llu\n", "Major page faults:", s->ac_majflt);
209     printf("%-25s%llu usec\n", "Scaled user time:", s->ac_utimescaled);
210     printf("%-25s%llu usec\n", "Scaled system time:", s->ac_stimescaled);
211
212     printf("\nDelay accounting\n");
213     printf("----------------\n");
214     printf("       %15s%15s%15s%15s%15s%15s\n",
215            "Count",
216            human_readable ? "Delay (ms)" : "Delay (ns)",
217            "Average delay",
218            "Real delay",
219            "Scaled real",
220            "Virtual delay");
221
222     if (!human_readable) {
223         printf("CPU    %15llu%15llu%15llu%15llu%15llu%15llu\n",
224                s->cpu_count,
225                s->cpu_delay_total,
226                average_ns(s->cpu_delay_total, s->cpu_count),
227                s->cpu_run_real_total,
228                s->cpu_scaled_run_real_total,
229                s->cpu_run_virtual_total);
230         printf("IO     %15llu%15llu%15llu\n",
231                s->blkio_count,
232                s->blkio_delay_total,
233                average_ns(s->blkio_delay_total, s->blkio_count));
234         printf("Swap   %15llu%15llu%15llu\n",
235                s->swapin_count,
236                s->swapin_delay_total,
237                average_ns(s->swapin_delay_total, s->swapin_count));
238         printf("Reclaim%15llu%15llu%15llu\n",
239                s->freepages_count,
240                s->freepages_delay_total,
241                average_ns(s->freepages_delay_total, s->freepages_count));
242     } else {
243         const double ms_per_ns = 1e6;
244         printf("CPU    %15llu%15.3f%15.3f%15.3f%15.3f%15.3f\n",
245                s->cpu_count,
246                s->cpu_delay_total / ms_per_ns,
247                average_ms(s->cpu_delay_total, s->cpu_count),
248                s->cpu_run_real_total / ms_per_ns,
249                s->cpu_scaled_run_real_total / ms_per_ns,
250                s->cpu_run_virtual_total / ms_per_ns);
251         printf("IO     %15llu%15.3f%15.3f\n",
252                s->blkio_count,
253                s->blkio_delay_total / ms_per_ns,
254                average_ms(s->blkio_delay_total, s->blkio_count));
255         printf("Swap   %15llu%15.3f%15.3f\n",
256                s->swapin_count,
257                s->swapin_delay_total / ms_per_ns,
258                average_ms(s->swapin_delay_total, s->swapin_count));
259         printf("Reclaim%15llu%15.3f%15.3f\n",
260                s->freepages_count,
261                s->freepages_delay_total / ms_per_ns,
262                average_ms(s->freepages_delay_total, s->freepages_count));
263     }
264
265     printf("\nExtended accounting fields\n");
266     printf("--------------------------\n");
267     if (human_readable && s->ac_stime) {
268         printf("%-25s%.3f MB\n", "Average RSS usage:",
269                (double)s->coremem / s->ac_stime);
270         printf("%-25s%.3f MB\n", "Average VM usage:",
271                (double)s->virtmem / s->ac_stime);
272     } else {
273         printf("%-25s%llu MB\n", "Accumulated RSS usage:", s->coremem);
274         printf("%-25s%llu MB\n", "Accumulated VM usage:", s->virtmem);
275     }
276     printf("%-25s%llu KB\n", "RSS high water mark:", s->hiwater_rss);
277     printf("%-25s%llu KB\n", "VM high water mark:", s->hiwater_vm);
278     printf("%-25s%llu\n", "IO bytes read:", s->read_char);
279     printf("%-25s%llu\n", "IO bytes written:", s->write_char);
280     printf("%-25s%llu\n", "IO read syscalls:", s->read_syscalls);
281     printf("%-25s%llu\n", "IO write syscalls:", s->write_syscalls);
282
283     printf("\nPer-task/thread statistics\n");
284     printf("--------------------------\n");
285     printf("%-25s%llu\n", "Voluntary switches:", s->nvcsw);
286     printf("%-25s%llu\n", "Involuntary switches:", s->nivcsw);
287 }
288
289 void print_usage() {
290   printf("Linux task stats reporting tool\n"
291          "\n"
292          "Usage: taskstats [options]\n"
293          "\n"
294          "Options:\n"
295          "  --help        This text\n"
296          "  --pid PID     Print stats for the process id PID\n"
297          "  --tgid TGID   Print stats for the thread group id TGID\n"
298          "  --raw         Print raw numbers instead of human readable units\n"
299          "\n"
300          "Either PID or TGID must be specified. For more documentation about "
301          "the reported fields, see\n"
302          "https://www.kernel.org/doc/Documentation/accounting/"
303          "taskstats-struct.txt\n");
304 }
305
306 int main(int argc, char** argv) {
307     int command_type = 0;
308     int pid = 0;
309     int human_readable = 1;
310
311     const struct option long_options[] = {
312         {"help", no_argument, 0, 0},
313         {"pid", required_argument, 0, 0},
314         {"tgid", required_argument, 0, 0},
315         {"raw", no_argument, 0, 0},
316         {0, 0, 0, 0}
317     };
318
319     while (1) {
320         int option_index;
321         int option_char = getopt_long_only(argc, argv, "", long_options,
322                                            &option_index);
323         if (option_char == -1) {
324             break;
325         }
326         switch (option_index) {
327             case 0:
328                 print_usage();
329                 return EXIT_SUCCESS;
330             case 1:
331                 command_type = TASKSTATS_CMD_ATTR_PID;
332                 pid = atoi(optarg);
333                 break;
334             case 2:
335                 command_type = TASKSTATS_CMD_ATTR_TGID;
336                 pid = atoi(optarg);
337                 break;
338             case 3:
339                 human_readable = 0;
340                 break;
341             default:
342                 break;
343         };
344     }
345
346     if (!pid) {
347         printf("Either PID or TGID must be specified\n");
348         return EXIT_FAILURE;
349     }
350
351     struct nl_sock* netlink_socket = nl_socket_alloc();
352     if (!netlink_socket || genl_connect(netlink_socket) < 0) {
353         perror("Unable to open netlink socket (are you root?)");
354         goto error;
355     }
356
357     int family_id = get_family_id(netlink_socket, TASKSTATS_GENL_NAME);
358     if (!family_id) {
359         perror("Unable to determine taskstats family id "
360                "(does your kernel support taskstats?)");
361         goto error;
362     }
363     struct TaskStatistics stats;
364     if (query_task_stats(netlink_socket, family_id, command_type, pid,
365                          &stats) < 0) {
366         perror("Failed to query taskstats");
367         goto error;
368     }
369     print_task_stats(&stats, human_readable);
370
371     nl_socket_free(netlink_socket);
372     return EXIT_SUCCESS;
373
374 error:
375     if (netlink_socket) {
376         nl_socket_free(netlink_socket);
377     }
378     return EXIT_FAILURE;
379 }