Home | History | Annotate | Download | only in taskstats
      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 }
    380