1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# Copyright (c) 2022 Huawei Device Co., Ltd. 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 16import sys 17 18MIN_DECL_LIST_LEN = 2 19 20 21def handle_macro(decl_str): 22 ''' 23 # Function: 24 # The declaration string may contain macro, 25 # This function expands it. it's defined as follow: 26 # #ifdef CONFIG_CPU_BIG_ENDIAN 27 # #define arg_u32p(name) u32, name##_hi, u32, name##_lo 28 # #else 29 # #define arg_u32p(name) u32, name##_lo, u32, name##_hi 30 # #endif 31 # find the macro 32 ''' 33 start = decl_str.find('arg_u32p') 34 if start == -1: 35 return decl_str 36 macro_str = decl_str[start:] 37 end = macro_str.find(')') 38 end += 1 39 macro_str = macro_str[:end] 40 macro_str_len = len(macro_str) 41 macro_list = macro_str.split('(') 42 var_name = macro_list[1] 43 var_name = var_name[:-1] 44 var_name = var_name.strip() 45 expansion = "u64, {}".format(var_name) 46 left = decl_str[:start] 47 right = decl_str[start + macro_str_len:] 48 decl_str = "{}{}{}".format(left, expansion, right) 49 return decl_str 50 51 52def get_decl_str(inf): 53 ''' 54 # Function: 55 # Get the declaration string of system call 56 # Arguments: 57 # @inf: input file 58 # Return: 59 # Declaration string of system call, e.g. 60 # do_sys_openat2(open, int, dfd, const char __user*, filename, struct open_how *, how) 61 ''' 62 decl_str = inf.readline() 63 if decl_str == "": 64 print("empty line read from input file") 65 return "" 66 decl_str = decl_str.strip() 67 if decl_str[0] == '"': 68 while True: 69 line = inf.readline() 70 line = line.strip() 71 decl_str = "{}{}".format(decl_str, line) 72 if (decl_str[-1] == '"'): 73 break 74 decl_str = decl_str[1:-1] 75 return handle_macro(decl_str) 76 77 78def get_decl_list(decl_str): 79 ''' 80 # Function: 81 # Split declaration string into list 82 # Arguments: 83 # @decl_str: system call declaration string, e.g. 84 # do_sys_openat2(open, int, dfd, const char __user*, filename, struct open_how *, how) 85 # Return: 86 # Declaration list, e.g. 87 # ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how'] 88 ''' 89 decl_list = decl_str.split('(') 90 if len(decl_list) < 2: 91 print("failed to split %s with '('" % decl_str) 92 return [] 93 probe_entry = decl_list[0] 94 probe_entry = probe_entry.strip() 95 args_str = decl_list[1] 96 args_str = args_str.strip() 97 # drop the tailing ')' 98 args_str = args_str[:-1] 99 args_list = args_str.split(',') 100 args_list = [args_item.strip() for args_item in args_list] 101 decl_list = [] 102 decl_list.append(probe_entry) 103 decl_list = decl_list + args_list 104 return decl_list 105 106 107def get_args_list(decl_list): 108 ''' 109 # Function: 110 # Truncate the prefix and system call entry name from the declaration list 111 # Arguments: 112 # @decl_list: Declaration list, e.g. 113 # ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how'] 114 # Return: 115 # Arguments list of system call, e.g. 116 # ['open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how'] 117 ''' 118 if len(decl_list) < MIN_DECL_LIST_LEN: 119 print("failed to get arguments list from %s" % decl_list) 120 return [] 121 return decl_list[MIN_DECL_LIST_LEN - 1:] 122 123 124def gen_kprobe_sec(decl_list): 125 ''' 126 # Function: 127 # Generate the kprobe prog type declaration, e.g. SEC("kprobe/__arm64_sys_open") 128 # Arguments: 129 # @decl_list: Declaration list, e.g. 130 # ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how'] 131 # Return: 132 # kprobe prog type declaration, e.g. SEC("kprobe/do_sys_openat2") 133 ''' 134 if len(decl_list) < MIN_DECL_LIST_LEN: 135 print("failed to generate kprobe section from %s" % decl_list) 136 return "" 137 probe_str = '\nSEC("kprobe/' 138 syscall_fn_index = 0 139 syscall = decl_list[syscall_fn_index] 140 probe_str = '{}{}")\n'.format(probe_str, syscall) 141 return probe_str 142 143 144def gen_kretprobe_sec(decl_list): 145 ''' 146 # Function: 147 # Generate the kretprobe prog type declaration, e.g. SEC("kretprobe/__arm64_sys_open") 148 # Arguments: 149 # @decl_list: Declaration list, e.g. 150 # ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how'] 151 # Return: 152 # kretprobe prog type declaration, e.g. SEC("kretprobe/do_sys_openat2") 153 ''' 154 if len(decl_list) < MIN_DECL_LIST_LEN: 155 print("failed to generate kretprobe section from %s" % decl_list) 156 return "" 157 probe_str = '\nSEC("kretprobe/' 158 syscall_fn_index = 0 159 syscall = decl_list[syscall_fn_index] 160 probe_str = '{}{}")\n'.format(probe_str, syscall) 161 return probe_str 162 163 164def gen_kprobe_decl(decl_list): 165 ''' 166 # Function: 167 # Generate the kprobe prog declaration, e.g. 168 # int BPF_KPROBE(do_sys_openat2_entry, int dfd, const char* filename, struct open_how* how) 169 # Arguments: 170 # @decl_list: Declaration list, e.g. 171 # ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how'] 172 # Return: 173 # kprobe prog declaration, e.g. 174 # int BPF_KPROBE(do_sys_openat2_entry, int dfd, const char __user* filename, struct open_how *how) 175 ''' 176 if len(decl_list) < MIN_DECL_LIST_LEN: 177 print("failed to generate kprobe prog declaration from %s" % decl_list) 178 return "" 179 prog_decl = "int BPF_KPROBE(" 180 syscall_fn_index = 0 181 prog_name = "{}_entry".format(decl_list[syscall_fn_index]) 182 prog_decl = "{}{}".format(prog_decl, prog_name) 183 index = MIN_DECL_LIST_LEN + 1 184 count = 0 185 max_nr_args = 5 186 while index < len(decl_list): 187 if count >= max_nr_args: 188 break 189 prog_decl = '%s, %s %s' % (prog_decl, decl_list[index - 1], decl_list[index]) 190 index += 2 191 count += 1 192 prog_decl = "{})".format(prog_decl) 193 return prog_decl 194 195 196def gen_kretprobe_decl(decl_list): 197 ''' 198 # Function: 199 # Generate the kretprobe prog declaration, e.g. 200 # int BPF_KRETPROBE(__arm64_sys_open_exit, long long ret) 201 # Arguments: 202 # @decl_list: Declaration list, e.g. 203 # ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how'] 204 # Return: 205 # kretprobe prog declaration, e.g. 206 # int BPF_KRETPROBE(do_sys_openat2_exit, long long ret) 207 ''' 208 if len(decl_list) < MIN_DECL_LIST_LEN: 209 print("failed to generate kretprobe prog declaration from %s" % decl_list) 210 return "" 211 prog_decl = "int BPF_KRETPROBE(" 212 syscall_fn_index = 0 213 prog_name = "{}_exit".format(decl_list[syscall_fn_index]) 214 prog_decl = "{}{}, int64_t retval)".format(prog_decl, prog_name) 215 return prog_decl 216 217HEAD_BPF_PROG_CODE = ( 218'/************************** fstrace BPF progs BEGIN *****************************/') 219 220TAIL_BPF_PROG_CODE = ( 221'/*************************** fstrace BPF progs END ******************************/\n') 222 223HEAD_COMMON_KPROBE_CODE = R''' 224{ 225 if (check_current_pid(-1, -1) != 0) { 226 // not any one of target processes, skip it 227 return 0; 228 } 229 struct start_event_t start_event = {}; 230 __builtin_memset(&start_event, 0, sizeof(start_event)); 231 struct fstrace_start_event_t *fs_se = &start_event.fs_se; 232 u64 pid_tgid = bpf_get_current_pid_tgid(); 233 // get timestamp of the start of system call 234 fs_se->stime = bpf_ktime_get_ns(); 235 // get argument of the system call 236''' 237 238TAIL_COMMON_KPROBE_CODE = R''' 239 // store the start event with pid as key 240 int err = (int) bpf_map_update_elem(&start_event_map, &pid_tgid, &start_event, BPF_ANY); 241 if (err != 0) { 242 BPFLOGE(BPF_TRUE, "failed to store fstrace start event"); 243 return -1; 244 } 245 return 0; 246} 247''' 248 249HEAD_COMMON_KRETPROBE_CODE = R''' 250{ 251 if (check_current_pid(-1, -1) != 0) { 252 // not any one of target processes, skip it 253 return 0; 254 } 255''' 256 257TAIL_COMMON_KRETPROBE_CODE = ''' 258} 259''' 260 261HIEBPF_TYPES_HEAD = R''' 262#ifndef FSTRACE_TYPES_H 263#define FSTRACE_TYPES_H 264 265#include "hiebpf_macros.h" 266 267''' 268 269HIEBPF_TYPES_TAIL = '#endif' 270 271 272def get_args_type(decl_list): 273 ''' 274 # Function: 275 # Get the arguments struct type, e.g. struct sys_pidfd_open_args_t 276 # Arguments: 277 # @decl_list: Declaration list, e.g. 278 # ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how'] 279 # Return: 280 # arguments struct type, e.g. struct sys_open_args_t 281 ''' 282 if len(decl_list) < MIN_DECL_LIST_LEN: 283 print("failed to get arguments type from %s" % decl_list) 284 return "" 285 args_type_index = 1 286 fn_name = decl_list[args_type_index] 287 args_type_str = "struct sys_{}_args_t".format(fn_name) 288 return args_type_str 289 290 291def get_arg_variable_name(decl_list): 292 ''' 293 # Function 294 # Get the arguments struct type instance name, e.g. pidfd_open_args 295 # Arguments: 296 # @decl_list: Declaration list, e.g. 297 # ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how'] 298 # Return: 299 # the arguments struct type instance name, e.g. open_args 300 ''' 301 if len(decl_list) < MIN_DECL_LIST_LEN: 302 print("failed to get arguments variable name from %s" % decl_list) 303 return "" 304 args_type_index = 1 305 return "{}_args".format(decl_list[args_type_index]) 306 307 308def get_arg_member_index(decl_list): 309 ''' 310 # Function: 311 # Get the member name list of arguments type 312 # Arguments: 313 # @decl_list: Declaration list, e.g. 314 # ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how'] 315 # Return: 316 # member name list of arguments type, e.g. ['pid', 'flags'] 317 ''' 318 member_index_list = [] 319 if len(decl_list) < MIN_DECL_LIST_LEN: 320 print("failed to get arguments member index list from %s" % decl_list) 321 return member_index_list 322 index = MIN_DECL_LIST_LEN + 1 323 count = 0 324 max_nr_args = 5 325 while index < len(decl_list): 326 if count >= max_nr_args: 327 break 328 member_index_list.append(index) 329 index += 2 330 count += 1 331 return member_index_list 332 333 334def gen_kprobe_code(decl_list): 335 ''' 336 # Function: 337 # Generate system call specific code of BPF kprobe prog 338 # Arguments: 339 # @decl_list: Declaration list, e.g. 340 # ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how'] 341 # Return: 342 # system call specific code string of BPF kprobe prog 343 ''' 344 indent = " " 345 args_type = get_args_type(decl_list) 346 if args_type == "": 347 return "" 348 args_var_name = get_arg_variable_name(decl_list) 349 if args_var_name == "": 350 return "" 351 args_type_index = 1 352 event_type = "SYS_{}".format(decl_list[args_type_index].upper()) 353 code_str = " fs_se->type = {};\n".format(event_type) 354 var_decl_str = "{}{}* args = &fs_se->{};\n".format(indent, args_type, args_var_name) 355 member_index_list = get_arg_member_index(decl_list) 356 assignment_code_str = "" 357 count = 0 358 max_nr_args = 5 359 for index in member_index_list: 360 if count >= max_nr_args: 361 break 362 member_name = decl_list[index] 363 assignment_str = "{}args->{} = {};\n".format(indent, member_name, member_name) 364 if member_name == "filename": 365 assignment_str = "{}{}emit_strtrace_event(fs_se->stime, fs_se->type, filename, FSTRACE);\n".format( 366 assignment_str, indent) 367 assignment_code_str = "{}{}".format(assignment_code_str, assignment_str) 368 count += 1 369 code_str = "{}{}{}".format(code_str, var_decl_str, assignment_code_str) 370 return code_str 371 372 373def gen_kretprobe_code(decl_list): 374 ''' 375 # Function: 376 # Generate system call specific code of BPF kretprobe prog 377 # Arguments: 378 # @decl_list: Declaration list, e.g. 379 # ['yes', '__arm64_sys_pidfd_open', 'pidfd_open', 'pid_t', 'pid', 'unsigned int', 'flags'] 380 # Return: 381 # system call specific code string of BPF kretprobe prog 382 ''' 383 if len(decl_list) < MIN_DECL_LIST_LEN: 384 print("failed to get system call type from %s" % decl_list) 385 return "" 386 fn_name = decl_list[2] 387 tracer = "FSTRACE" 388 return " return emit_event(ctx, retval, {});".format(tracer) 389 390 391def underscore_int_types(arg_type): 392 # underscore u8 393 arg_type = arg_type.strip() 394 arg_type_list = arg_type.split(' ') 395 for index , arg_item in enumerate(arg_type_list): 396 if arg_item == "u8": 397 arg_type_list[index] = "__u8" 398 break 399 if arg_item == "u16": 400 arg_type_list[index] = "__u16" 401 break 402 if arg_item == "u32": 403 arg_type_list[index] = "__u32" 404 break 405 if arg_item == "u64": 406 arg_type_list[index] = "__u64" 407 break 408 if arg_item == "s8": 409 arg_type_list[index] = "__s8" 410 break 411 if arg_item == "s16": 412 arg_type_list[index] = "__s16" 413 break 414 if arg_item == "s32": 415 arg_type_list[index] = "__s32" 416 break 417 if arg_item == "s64": 418 arg_type_list[index] = "__s64" 419 break 420 arg_type = arg_type_list[0] 421 for index, arg_item in enumerate(arg_type_list): 422 if index == 0: 423 continue 424 arg_type = "%s %s" % (arg_type, arg_type_list[index]) 425 return arg_type 426 427 428def gen_struct_str(args_list): 429 ''' 430 # Function: 431 # Generate arguments type struct, e.g. 432 # struct sys_open_args_t { 433 # const char __user* filename; 434 # int flags; 435 # umode_t mode; 436 # } 437 # Arguments: 438 # @args_list: arguments list of system call, e.g. 439 # ['do_sys_openat2', 'int', 'dfd', 'unsigned int', 'flags'] 440 # Return: 441 # arguments type struct, e.g. 442 # struct sys_open_args_t { 443 # const char __user* filename; 444 # int flags; 445 # umode_t mode; 446 # } 447 ''' 448 length = len(args_list) 449 if length < 1: 450 return "" 451 result = "struct " 452 index = 0 453 while index < length: 454 if index == 0: 455 fn_name = args_list[index] 456 fn_name = "sys_%s_args_t {\n" % (fn_name) 457 result = "{}{}".format(result, fn_name) 458 index = index + 1 459 else: 460 arg_type = underscore_int_types(args_list[index]) 461 index = index + 1 462 if index >= length: 463 return "" 464 arg_name = args_list[index] 465 index = index + 1 466 result = "{} {} {};\n".format(result, arg_type, arg_name) 467 result = "%s};\n" % (result) 468 return result 469 470 471def output_fstrace_code(fstrace_progs_file, fstrace_types_file, fstrace_targets_file): 472 with open(fstrace_progs_file, 'a') as progs_output_file: 473 progs_output_file.write(HEAD_BPF_PROG_CODE) 474 with open(fstrace_types_file, 'a') as args_output_file: 475 args_output_file.write(HIEBPF_TYPES_HEAD) 476 with open(fstrace_targets_file, 'r') as inf: 477 type_defs_set = set() 478 fn_names_set = set() 479 start_event_def = ( 480 "struct fstrace_start_event_t {\n" 481 " __u32 type;\n" 482 " __u64 stime;\n" 483 " union {\n") 484 start_event_def_end = " };\n};\n\n" 485 arg_types_enum_def = "enum FSTraceEventType:__u32 {\n" 486 arg_types_enum_def_end = "};\n" 487 count = 1 488 while True: 489 # get declaration list, arguments list and arguments definition string 490 decl_str = get_decl_str(inf) 491 if decl_str == "": 492 break 493 decl_list = get_decl_list(decl_str) 494 args_list = get_args_list(decl_list) 495 type_def_str = gen_struct_str(args_list) 496 fn_name = args_list[0] 497 # save arguments type definitions 498 if type_def_str not in type_defs_set: 499 type_defs_set.add(type_def_str) 500 type_def_lines = type_def_str.split('\n') 501 max_type_def_lines = 7 502 type_def_lines_reduced = type_def_lines 503 if len(type_def_lines) > max_type_def_lines: 504 type_def_lines_reduced = type_def_lines[:(max_type_def_lines - 1)] 505 type_def_lines_reduced.append("};\n") 506 type_def_str_reduced = "" 507 for type_def_line in type_def_lines_reduced: 508 type_def_str_reduced = "{}{}\n".format(type_def_str_reduced, type_def_line) 509 args_output_file.write(type_def_str_reduced) 510 start_event_def = "{} struct sys_{}_args_t {}_args;\n".format(start_event_def, 511 fn_name, fn_name) 512 if count == 1: 513 arg_type = "SYS_{} = 1".format(fn_name.upper()) 514 count = 0 515 else: 516 arg_type = "SYS_{}".format(fn_name.upper()) 517 arg_types_enum_def = "{} {},\n".format(arg_types_enum_def, arg_type) 518 519 # generate kprobe prog 520 kprobe_sec = gen_kprobe_sec(decl_list) 521 krpobe_decl = gen_kprobe_decl(decl_list) 522 kprobe_code = gen_kprobe_code(decl_list) 523 524 # save kprobe prog 525 progs_output_file.write(kprobe_sec) 526 progs_output_file.write(krpobe_decl) 527 progs_output_file.write(HEAD_COMMON_KPROBE_CODE) 528 progs_output_file.write(kprobe_code) 529 progs_output_file.write(TAIL_COMMON_KPROBE_CODE) 530 531 # generate kretprobe prog 532 kretprobe_sec = gen_kretprobe_sec(decl_list) 533 kretprobe_decl = gen_kretprobe_decl(decl_list) 534 kretprobe_code = gen_kretprobe_code(decl_list) 535 536 # save kretprobe prog 537 progs_output_file.write(kretprobe_sec) 538 progs_output_file.write(kretprobe_decl) 539 progs_output_file.write(HEAD_COMMON_KRETPROBE_CODE) 540 progs_output_file.write(kretprobe_code) 541 progs_output_file.write(TAIL_COMMON_KRETPROBE_CODE) 542 start_event_def = "{}{}".format(start_event_def, start_event_def_end) 543 arg_types_enum_def = "{}{}".format(arg_types_enum_def, arg_types_enum_def_end) 544 args_output_file.write(start_event_def) 545 args_output_file.write(arg_types_enum_def) 546 args_output_file.write(HIEBPF_TYPES_TAIL) 547 progs_output_file.write(TAIL_BPF_PROG_CODE) 548 progs_output_file.write("\n\n") 549 return 550