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 }