rocnikovy-projekt

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

main.c (13592B)


      1 #include <errno.h>
      2 #include <getopt.h>
      3 #include <signal.h>
      4 #include <stdint.h>
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <sys/wait.h>
      8 #include <unistd.h>
      9 #include <bpf/libbpf.h>
     10 #include "process_exit.h"
     11 #include "process_exit.skel.h"
     12 
     13 #define PCOMM_FLAG    0x00001
     14 #define TGID_FLAG     0x00002
     15 #define PID_FLAG      0x00004
     16 #define PPID_FLAG     0x00008
     17 #define UID_FLAG      0x00010
     18 #define AGE_FLAG      0x00020
     19 #define UTIME_FLAG    0x00040
     20 #define STIME_FLAG    0x00080
     21 #define EXIT_FLAG     0x00100
     22 #define EXITSIG_FLAG  0x00200
     23 #define NVCS_FLAG     0x00400
     24 #define NIVCS_FLAG    0x00800
     25 #define CUTIME_FLAG   0x01000
     26 #define CSTIME_FLAG   0x02000
     27 #define INBLOCK_FLAG  0x04000
     28 #define OUBLOCK_FLAG  0x08000
     29 #define CINBLOCK_FLAG 0x10000
     30 #define COUBLOCK_FLAG 0x20000
     31 
     32 static int libbpf_print_fn(enum libbpf_print_level level,
     33                            const char* format, va_list args) {
     34     if (level >= LIBBPF_DEBUG)
     35         return 0;
     36 
     37     return vfprintf(stderr, format, args);
     38 }
     39 
     40 static int csv = 0;
     41 static FILE* output_file = 0;
     42 static uint32_t parameter_flags = 0x3ffff; // defaultne vsetky parametre
     43 static int enabled_flag_count = 0;
     44 static int current_flags_printed = 1;
     45 static uint64_t processes_handled = 0;
     46 static int cumulative = 0;
     47 static volatile __sig_atomic_t running = 1;
     48 static uint32_t filtered_uid = 0;
     49 
     50 static void parse_filter_flags(char* flag_string) {
     51     const char* token = strtok(flag_string, ",");
     52     while (token) {
     53         if      (strcmp(token, "pcomm")    == 0) parameter_flags |= PCOMM_FLAG;
     54         else if (strcmp(token, "tgid")     == 0) parameter_flags |= TGID_FLAG;
     55         else if (strcmp(token, "pid")      == 0) parameter_flags |= PID_FLAG;
     56         else if (strcmp(token, "ppid")     == 0) parameter_flags |= PPID_FLAG;
     57         else if (strcmp(token, "uid")      == 0) parameter_flags |= UID_FLAG;
     58         else if (strcmp(token, "age")      == 0) parameter_flags |= AGE_FLAG;
     59         else if (strcmp(token, "utime")    == 0) parameter_flags |= UTIME_FLAG;
     60         else if (strcmp(token, "stime")    == 0) parameter_flags |= STIME_FLAG;
     61         else if (strcmp(token, "exit")     == 0) parameter_flags |= EXIT_FLAG;
     62         else if (strcmp(token, "exitsig")  == 0) parameter_flags |= EXITSIG_FLAG;
     63         else if (strcmp(token, "nvcs")     == 0) parameter_flags |= NVCS_FLAG;
     64         else if (strcmp(token, "nivcs")    == 0) parameter_flags |= NIVCS_FLAG;
     65         else if (strcmp(token, "cutime")   == 0) parameter_flags |= CUTIME_FLAG;
     66         else if (strcmp(token, "cstime")   == 0) parameter_flags |= CSTIME_FLAG;
     67         else if (strcmp(token, "inblock")  == 0) parameter_flags |= INBLOCK_FLAG;
     68         else if (strcmp(token, "oublock")  == 0) parameter_flags |= OUBLOCK_FLAG;
     69         else if (strcmp(token, "cinblock") == 0) parameter_flags |= CINBLOCK_FLAG;
     70         else if (strcmp(token, "coublock") == 0) parameter_flags |= COUBLOCK_FLAG;
     71         else                                     fprintf(stderr, "Unknown parameter '%s'\n", token);
     72 
     73         token = strtok(NULL, ",");
     74     }
     75 }
     76 
     77 static void print_help(char* argv[]) {
     78     fprintf(stdout,
     79             "Usage: %s [OPTION]...\n"
     80             "Print information about terminated processes.\n"
     81             "\n"
     82             "Options:\n"
     83             "  -c, --csv                          use csv format\n"
     84             "  -f FILE, --file FILE               print to file FILE\n"
     85             "  -F params..., --filter params...   filter process information based on\n"
     86             "                                     params\n"
     87             "  -u UID, --uid UID                  show only processes owened by user\n"
     88             "                                     with uid UID\n"
     89             "  -C, --cumulative                   show cumulative data for utime,\n"
     90             "                                     stime, inblock, oublock, filters\n"
     91             "                                     out cutime, cstime, cinblock,\n"
     92             "                                     coublock\n"
     93             "  -h, --help                         print this help message and exit\n"
     94             "\n"
     95             "Filter parameters:\n"
     96             "  pcomm, tgid, pid, ppid, uid, age, utime, stime, exit, exitsig, nvcs,\n"
     97             "  nivcs, cutime, cstime, inblock, oublock, cinblock, coublock\n",
     98             argv[0]);
     99 }
    100 
    101 static void parse_options(int argc, char* argv[], const char** file) {
    102     static struct option long_options[] = {
    103         { "csv"        ,       no_argument, 0, 'c' },
    104         { "file"       , required_argument, 0, 'f' },
    105         { "filter"     , required_argument, 0, 'F' },
    106         { "cumulative" ,       no_argument, 0, 'C' },
    107         { "help"       ,       no_argument, 0, 'h' },
    108         { "uid"        , required_argument, 0, 'u' },
    109         { 0            ,                 0, 0,   0 },
    110     };
    111 
    112     int ret = EXIT_FAILURE;
    113     int opt;
    114     while ((opt = getopt_long(argc, argv, "cf:hF:Cu:", long_options, NULL)) != -1) {
    115         switch (opt) {
    116         case 'c':
    117             csv = 1;
    118             break;
    119         case 'f':
    120             *file = optarg;
    121             break;
    122         case 'F':
    123             parameter_flags = 0;
    124             parse_filter_flags(optarg);
    125             break;
    126         case 'C':
    127             cumulative = 1;
    128             break;
    129         case 'u':
    130             errno = 0;
    131             filtered_uid = strtoul(optarg, NULL, 10);
    132             if (errno != 0 || optarg[0] == '-') {
    133                 fprintf(stderr, "Invalid UID after -u.\n");
    134                 exit(EXIT_FAILURE);
    135             }
    136             break;
    137         case 'h':
    138             ret = EXIT_SUCCESS;
    139         default:
    140             print_help(argv);
    141             exit(ret);
    142         }
    143     }
    144 }
    145 
    146 static void print_int_parameter2(__u64 parameter, const char* format) {
    147     if (csv) {
    148         format = "%d";
    149         if (current_flags_printed < enabled_flag_count) {
    150             format = "%d,";
    151             current_flags_printed++;
    152         } else {
    153             current_flags_printed = 1;
    154         }
    155     }
    156     fprintf(output_file, format, parameter);
    157 }
    158 
    159 static void print_int_parameter(__u64 parameter) {
    160     print_int_parameter2(parameter, "%-7d\t");
    161 }
    162 
    163 static void print_float_parameter(float parameter) {
    164     const char* format = "%-7.3f\t";
    165     if (csv) {
    166         format = "%.3f";
    167         if (current_flags_printed < enabled_flag_count) {
    168             format = "%.3f,";
    169             current_flags_printed++;
    170         } else {
    171             current_flags_printed = 1;
    172         }
    173     }
    174     fprintf(output_file, format, parameter);
    175 }
    176 
    177 static void print_pcomm_parameter(const char* parameter) {
    178     const char* format = "%-16s";
    179     if (csv) {
    180         format = "%s";
    181         if (current_flags_printed < enabled_flag_count) {
    182             format = "%s,";
    183             current_flags_printed++;
    184         } else {
    185             current_flags_printed = 1;
    186         }
    187     }
    188     fprintf(output_file, format, parameter);
    189 }
    190 
    191 static void print_string_parameter(const char* parameter) {
    192     const char* format = "%s\t";
    193     if (csv) {
    194         format = "%s";
    195         if (current_flags_printed < enabled_flag_count) {
    196             format = "%s,";
    197             current_flags_printed++;
    198         } else {
    199             current_flags_printed = 1;
    200         }
    201     }
    202     fprintf(output_file, format, parameter);
    203 }
    204 
    205 static void handle_event(void* ctx, int cpu, void* data, unsigned int data_sz) {
    206     struct data_t* event = (struct data_t*)data;
    207     (void)ctx;
    208     (void)cpu;
    209     (void)data_sz;
    210 
    211     if (filtered_uid != 0 && filtered_uid != event->uid) {
    212         return;
    213     }
    214 
    215     if (cumulative) {
    216         event->utime += event->cutime;
    217         event->stime += event->cstime;
    218         event->inblock += event->cinblock;
    219         event->oublock += event->coublock;
    220     }
    221 
    222     processes_handled++;
    223 
    224     if (parameter_flags & PCOMM_FLAG) print_pcomm_parameter(event->task);
    225     if (parameter_flags & TGID_FLAG) print_int_parameter(event->tgid);
    226     if (parameter_flags & PID_FLAG) print_int_parameter(event->pid);
    227     if (parameter_flags & PPID_FLAG) print_int_parameter(event->ppid);
    228     if (parameter_flags & UID_FLAG) print_int_parameter(event->uid);
    229     if (parameter_flags & AGE_FLAG) print_float_parameter((event->exit_time - event->start_time) / 1e9);
    230     if (parameter_flags & UTIME_FLAG) print_float_parameter(event->utime / 1e9);
    231     if (parameter_flags & STIME_FLAG) print_float_parameter(event->stime / 1e9);
    232     if (parameter_flags & EXIT_FLAG) print_int_parameter(WIFEXITED(event->exit_code) ? WEXITSTATUS(event->exit_code) : -1);
    233     if (parameter_flags & EXITSIG_FLAG) print_int_parameter(WIFSIGNALED(event->exit_code) ? WTERMSIG(event->exit_code) : -1);
    234     if (parameter_flags & NVCS_FLAG) print_int_parameter(event->nvcsw);
    235     if (parameter_flags & NIVCS_FLAG) print_int_parameter(event->nivcsw);
    236     if (parameter_flags & CUTIME_FLAG) print_float_parameter(event->cutime / 1e9);
    237     if (parameter_flags & CSTIME_FLAG) print_float_parameter(event->cstime / 1e9);
    238     if (parameter_flags & INBLOCK_FLAG) print_int_parameter(event->inblock);
    239     if (parameter_flags & OUBLOCK_FLAG) print_int_parameter(event->oublock);
    240     if (parameter_flags & CINBLOCK_FLAG) print_int_parameter(event->cinblock);
    241     if (parameter_flags & COUBLOCK_FLAG) print_int_parameter(event->coublock);
    242     fprintf(output_file, "\n");
    243 
    244     fflush(output_file);
    245 }
    246 
    247 static void lost_event(void* ctx, int cpu, long long unsigned cnt) {
    248     (void)ctx;
    249     (void)cpu;
    250     (void)cnt;
    251 
    252     printf("lost event\n");
    253 }
    254 
    255 static void print_header(void) {
    256     if (parameter_flags & PCOMM_FLAG   ) print_pcomm_parameter("PCOMM");
    257     if (parameter_flags & TGID_FLAG    ) print_string_parameter("TGID");
    258     if (parameter_flags & PID_FLAG     ) print_string_parameter("PID");
    259     if (parameter_flags & PPID_FLAG    ) print_string_parameter("PPID");
    260     if (parameter_flags & UID_FLAG     ) print_string_parameter("UID");
    261     if (parameter_flags & AGE_FLAG     ) print_string_parameter("age");
    262     if (parameter_flags & UTIME_FLAG   ) print_string_parameter("utime");
    263     if (parameter_flags & STIME_FLAG   ) print_string_parameter("stime");
    264     if (parameter_flags & EXIT_FLAG    ) print_string_parameter("exit");
    265     if (parameter_flags & EXITSIG_FLAG ) print_string_parameter("exitsig");
    266     if (parameter_flags & NVCS_FLAG    ) print_string_parameter("nvcsw");
    267     if (parameter_flags & NIVCS_FLAG   ) print_string_parameter("nivcsw");
    268     if (parameter_flags & CUTIME_FLAG  ) print_string_parameter("cutime");
    269     if (parameter_flags & CSTIME_FLAG  ) print_string_parameter("cstime");
    270     if (parameter_flags & INBLOCK_FLAG ) print_string_parameter("inblk");
    271     if (parameter_flags & OUBLOCK_FLAG ) print_string_parameter("oublk");
    272     if (parameter_flags & CINBLOCK_FLAG) print_string_parameter("cinblk");
    273     if (parameter_flags & COUBLOCK_FLAG) print_string_parameter("coublk");
    274     fprintf(output_file, "\n");
    275 
    276     fflush(output_file);
    277 }
    278 
    279 static void catch_interrupt(int signal) {
    280     (void)signal;
    281     running = 0;
    282 }
    283 
    284 int main(int argc, char* argv[]) {
    285 
    286     output_file = stdout;
    287     const char* file_name = NULL;
    288     parse_options(argc, argv, &file_name);
    289     if ((enabled_flag_count = __builtin_popcount(parameter_flags)) == 0) {
    290         fprintf(stderr, "No valid parameters in filter\n");
    291         exit(EXIT_FAILURE);
    292     }
    293 
    294     if (cumulative) {
    295         // pri kumulativnom vypise nahradime hodnoty suctom
    296         // rodicovskych a dcerskych hodnot, preto nebudeme vypisovat
    297         // cutime, cstime, atd.
    298         parameter_flags &= ~CUTIME_FLAG;
    299         parameter_flags &= ~CSTIME_FLAG;
    300         parameter_flags &= ~CINBLOCK_FLAG;
    301         parameter_flags &= ~COUBLOCK_FLAG;
    302         enabled_flag_count -= 4;
    303     }
    304 
    305     if (getuid() != 0) {
    306         fprintf(stderr, "You should run this program with root privileges (sudo).\n");
    307         exit(EXIT_FAILURE);
    308     }
    309 
    310     libbpf_set_print(libbpf_print_fn);
    311 
    312     struct process_exit_bpf* skel = process_exit_bpf__open_and_load();
    313     if (!skel) {
    314         fprintf(stderr, "Failed to open BPF object file\n");
    315         return 1;
    316     }
    317 
    318     int err = process_exit_bpf__attach(skel);
    319     if (err) {
    320         fprintf(stderr, "Failed to attach BPF skeleton: %d\n", err);
    321         process_exit_bpf__destroy(skel);
    322         return 1;
    323     }
    324 
    325     struct perf_buffer* pb = perf_buffer__new(bpf_map__fd(skel->maps.output),
    326                                               8,
    327                                               handle_event,
    328                                               lost_event,
    329                                               NULL,
    330                                               NULL);
    331     if (!pb) {
    332         err = -1;
    333         fprintf(stderr, "Failed to create ring buffer\n");
    334         process_exit_bpf__destroy(skel);
    335         return -err;
    336     }
    337 
    338     if (signal(SIGINT, catch_interrupt) == SIG_ERR) {
    339         fprintf(stderr, "Can't set signal handler: %s\n", strerror(errno));
    340         err = 1;
    341         running = 0;
    342     }
    343 
    344     int writing_to_file = 0;
    345     if (file_name != NULL && strcmp(file_name, "-") != 0) {
    346         output_file = fopen(file_name, "w+");
    347         writing_to_file = 1;
    348     }
    349 
    350     print_header();
    351     while (running) {
    352         err = perf_buffer__poll(pb, 1000);
    353         if (err == -EINTR) {
    354             err = 0;
    355             break;
    356         }
    357         if (err < 0) {
    358             printf("Error polling perf buffer: %d\n", err);
    359             break;
    360         }
    361     }
    362 
    363     printf("\nHandled %lu processes.\n", processes_handled);
    364     fflush(stdout);
    365 
    366     if (writing_to_file) {
    367         fclose(output_file);
    368     }
    369 
    370     perf_buffer__free(pb);
    371     process_exit_bpf__destroy(skel);
    372 
    373     return -err;
    374 }