Control: retitle -1 ltrace: [p]{read,write}[v]() handling, common *64() functions in modern glibc, [fl]seek[o]()/ftell[o](); requisite [u]llong; 20x speed optimisation in default config; format %b+bin() lens and %w[f]32x; splice/c_f_r/sendfile[64] formatting; useful __errno_location; %config lines not limited to 1kB; pipe[2]() with fds; sysconf(); %m[un]map[64](); operator new/delete; <regex.h>; another ~10%
I'm attaching everything, since I fixed some documentation fucky-wuckies from the earlier ones. 0018-0021 are new, and turn $ grep pipe ll pipe2(0x7ffcd6d7baf0, 0x80000, 0x7ffcd6d7bb70, 0x7ffcd6d7bb70) = 0 $ grep -e mmap -e sysconf ll sysconf(30, 0, 1, 0x393c) = 4096 mmap64(0, 0x3d3c, 1, 2) = 0x7fe67b29c000 into $ grep pipe ll pipe2([3, 4], 0x80000) = 0 $ grep -e mmap -e sysconf ll sysconf(_SC_PAGE_SIZE) = 4096 mmap64(0, 15676, 0b1, 0x2, 0, 4096) = 0x7fa1b27ff000 regex.h suite looks like: regcomp(0x7fff5ee83120, ";", 0b100) = REG_OK regexec(0x7fff5ee83040, "\nint SYS_access(string,octal);"..., 1, [{0, 31}], 0b111) = REG_NOMATCH regexec(0x7fff5ee83040, ";\nint SYS_access(string,octal)"..., 1, [{0, 32}], 0b111) = REG_OK regcomp(0x7ffece59ee70, "\\(", 0b100) = REG_EPAREN regerror(REG_EPAREN, 0x7ffece59ee70, nil, 0) = 18 malloc(18) = 0x5604c35c2140 regerror(REG_EPAREN, 0x7ffece59ee70, "Unmatched ( or \\(", 18) = 18 And function lookups are sped up approximately ten-fold (3μs -> 300ns on a hit (depending on the access pattern, of course) and 12μs -> 600ns on a miss) which translates to an approximately 10% (roughly, maybe, it's difficult to measure) speed-up depending on the program: $ hyperfine '/bin/ltrace -o/dev/null -F /etc/ltrace.conf ls' \ './ltrace -o/dev/null -F /etc/ltrace.conf ls' Benchmark 1: /bin/ltrace -o/dev/null -F /etc/ltrace.conf ls Time (mean ± σ): 545.6 ms ± 75.8 ms [User: 153.6 ms, System: 350.1 ms] Range (min … max): 473.1 ms … 722.5 ms 10 runs Benchmark 2: ./ltrace -o/dev/null -F /etc/ltrace.conf ls Time (mean ± σ): 447.1 ms ± 36.7 ms [User: 94.6 ms, System: 329.8 ms] Range (min … max): 412.8 ms … 516.4 ms 10 runs Summary './ltrace -o/dev/null -F /etc/ltrace.conf ls' ran 1.22 ± 0.20 times faster than '/bin/ltrace -o/dev/null -F /etc/ltrace.conf ls' vs $ hyperfine '/bin/ltrace -F /etc/ltrace.conf -o/dev/null ~/code/voreutils/out/cmd/tac -rs \; < etc/ltrace.conf' \ './ltrace -F /etc/ltrace.conf -o/dev/null ~/code/voreutils/out/cmd/tac -rs \; < etc/ltrace.conf' Benchmark 1: /bin/ltrace -F /etc/ltrace.conf -o/dev/null ~/code/voreutils/out/cmd/tac -rs \; < etc/ltrace.conf Time (mean ± σ): 3.631 s ± 0.117 s [User: 1.087 s, System: 2.334 s] Range (min … max): 3.516 s … 3.886 s 10 runs Benchmark 2: ./ltrace -F /etc/ltrace.conf -o/dev/null ~/code/voreutils/out/cmd/tac -rs \; < etc/ltrace.conf Time (mean ± σ): 3.113 s ± 0.169 s [User: 0.659 s, System: 2.274 s] Range (min … max): 2.934 s … 3.496 s 10 runs Summary './ltrace -F /etc/ltrace.conf -o/dev/null ~/code/voreutils/out/cmd/tac -rs \; < etc/ltrace.conf' ran 1.17 ± 0.07 times faster than '/bin/ltrace -F /etc/ltrace.conf -o/dev/null ~/code/voreutils/out/cmd/tac -rs \; < etc/ltrace.conf'
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Tue, 25 Jul 2023 21:20:32 +0200 Subject: Add llong/ullong. Tested on i686/amd64 --- expr.c | 6 +++--- expr.h | 4 ++-- lens_default.c | 14 ++++++++++---- printf.c | 10 +++++----- read_config_file.c | 4 ++++ sysdeps/linux-gnu/ia64/fetch.c | 4 ++++ sysdeps/linux-gnu/m68k/fetch.c | 2 ++ sysdeps/linux-gnu/ppc/fetch.c | 2 ++ sysdeps/linux-gnu/ppc/trace.c | 6 ++++++ sysdeps/linux-gnu/s390/fetch.c | 2 ++ sysdeps/linux-gnu/s390/trace.c | 6 ++++++ sysdeps/linux-gnu/x86/fetch.c | 15 ++++++++++++--- sysdeps/linux-gnu/x86/trace.c | 6 ++++++ type.c | 25 ++++++++++++++++++++++--- type.h | 2 ++ value.c | 16 ++++++++-------- value.h | 4 ++-- zero.c | 2 +- 18 files changed, 99 insertions(+), 31 deletions(-) diff --git a/expr.c b/expr.c index 32860fd..01ce4c6 100644 --- a/expr.c +++ b/expr.c @@ -246,7 +246,7 @@ eval_index(struct expr_node *node, struct value *context, if (expr_eval(node->lhs, context, arguments, &lhs) < 0) return -1; - long l; + long long l; if (expr_eval_word(node->u.node.n, context, arguments, &l) < 0) { fail: value_destroy(&lhs); @@ -305,7 +305,7 @@ expr_eval(struct expr_node *node, struct value *context, int expr_eval_word(struct expr_node *node, struct value *context, - struct value_dict *arguments, long *ret_value) + struct value_dict *arguments, long long *ret_value) { struct value val; if (expr_eval(node, context, arguments, &val) < 0) @@ -318,7 +318,7 @@ expr_eval_word(struct expr_node *node, struct value *context, } int -expr_eval_constant(struct expr_node *node, long *valuep) +expr_eval_constant(struct expr_node *node, long long *valuep) { assert(expr_is_compile_constant(node)); return expr_eval_word(node, NULL, NULL, valuep); diff --git a/expr.h b/expr.h index 53b75b7..aa4b0b6 100644 --- a/expr.h +++ b/expr.h @@ -135,7 +135,7 @@ int expr_eval(struct expr_node *node, struct value *context, /* Evaluate compile-time expression. Returns 0 on success or negative * value on failure. Computed value is passed back in *VALUEP. */ -int expr_eval_constant(struct expr_node *node, long *valuep); +int expr_eval_constant(struct expr_node *node, long long *valuep); /* Evaluate expression, whose result should fit into a word. In order * to easily support all the structure and array accesses, we simply @@ -143,7 +143,7 @@ int expr_eval_constant(struct expr_node *node, long *valuep); * to be able to get out a word-size datum to use it as an index, a * length, etc. */ int expr_eval_word(struct expr_node *node, struct value *context, - struct value_dict *arguments, long *ret_value); + struct value_dict *arguments, long long *ret_value); /* Returns non-zero value if the expression is a compile-time * constant. Currently this is only EXPR_OP_CONST, but eventually diff --git a/lens_default.c b/lens_default.c index 36531c2..2fd4915 100644 --- a/lens_default.c +++ b/lens_default.c @@ -58,7 +58,7 @@ READER(read_double, double) #define HANDLE_WIDTH(BITS) \ do { \ - long l; \ + long long l; \ if (value_extract_word(value, &l, arguments) < 0) \ return -1; \ int##BITS##_t i = l; \ @@ -124,7 +124,7 @@ acc_fprintf(int *countp, FILE *stream, const char *format, ...) static int format_char(FILE *stream, struct value *value, struct value_dict *arguments) { - long lc; + long long lc; if (value_extract_word(value, &lc, arguments) < 0) return -1; int c = (int)lc; @@ -344,8 +344,8 @@ format_array(FILE *stream, struct value *value, struct value_dict *arguments, { /* We need "long" to be long enough to cover the whole address * space. */ - (void)sizeof(char[1 - 2*(sizeof(long) < sizeof(void *))]); - long l; + (void)sizeof(char[1 - 2*(sizeof(long long) < sizeof(void *))]); + long long l; if (expr_eval_word(length, value, arguments, &l) < 0) return -1; size_t len = (size_t)l; @@ -394,11 +394,13 @@ toplevel_format_lens(struct lens *lens, FILE *stream, case ARGTYPE_SHORT: case ARGTYPE_INT: case ARGTYPE_LONG: + case ARGTYPE_LLONG: return format_integer(stream, value, int_fmt, arguments); case ARGTYPE_USHORT: case ARGTYPE_UINT: case ARGTYPE_ULONG: + case ARGTYPE_ULLONG: if (int_fmt == INT_FMT_i || int_fmt == INT_FMT_default) int_fmt = INT_FMT_u; return format_integer(stream, value, int_fmt, arguments); @@ -520,9 +522,11 @@ bool_lens_format_cb(struct lens *lens, FILE *stream, case ARGTYPE_SHORT: case ARGTYPE_INT: case ARGTYPE_LONG: + case ARGTYPE_LLONG: case ARGTYPE_USHORT: case ARGTYPE_UINT: case ARGTYPE_ULONG: + case ARGTYPE_ULLONG: case ARGTYPE_CHAR: if ((zero = value_is_zero(value, arguments)) < 0) return -1; @@ -578,9 +582,11 @@ string_lens_format_cb(struct lens *lens, FILE *stream, case ARGTYPE_SHORT: case ARGTYPE_INT: case ARGTYPE_LONG: + case ARGTYPE_LLONG: case ARGTYPE_USHORT: case ARGTYPE_UINT: case ARGTYPE_ULONG: + case ARGTYPE_ULLONG: return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_default); diff --git a/printf.c b/printf.c index 9aa68a5..773d104 100644 --- a/printf.c +++ b/printf.c @@ -35,7 +35,7 @@ struct param_enum { struct value array; int percent; - size_t *future_length; + unsigned long long *future_length; char *format; char const *ptr; char const *end; @@ -101,15 +101,15 @@ form_next_param(struct param_enum *self, struct arg_type_info *infop) { /* XXX note: Some types are wrong because we lack - ARGTYPE_LONGLONG, ARGTYPE_UCHAR and ARGTYPE_SCHAR. */ + ARGTYPE_UCHAR and ARGTYPE_SCHAR. */ assert(lng <= 2); assert(hlf <= 2); static enum arg_type ints[] = { ARGTYPE_CHAR, ARGTYPE_SHORT, ARGTYPE_INT, - ARGTYPE_LONG, ARGTYPE_ULONG }; + ARGTYPE_LONG, ARGTYPE_LLONG }; static enum arg_type uints[] = { ARGTYPE_CHAR, ARGTYPE_USHORT, ARGTYPE_UINT, - ARGTYPE_ULONG, ARGTYPE_ULONG }; + ARGTYPE_ULONG, ARGTYPE_ULLONG }; struct arg_type_info *elt_info = NULL; if (format_type == ARGTYPE_ARRAY || format_type == ARGTYPE_POINTER) @@ -343,7 +343,7 @@ static enum param_status param_printf_stop(struct param_enum *self, struct value *value) { if (self->future_length != NULL - && value_extract_word(value, (long *)self->future_length, NULL) < 0) + && value_extract_word(value, (long long *)self->future_length, NULL) < 0) drop_future_length(self); return PPCB_CONT; diff --git a/read_config_file.c b/read_config_file.c index e247436..98278ac 100644 --- a/read_config_file.c +++ b/read_config_file.c @@ -79,6 +79,8 @@ parse_arg_type(char **name, enum arg_type *ret) KEYWORD("uint", ARGTYPE_UINT); KEYWORD("long", ARGTYPE_LONG); KEYWORD("ulong", ARGTYPE_ULONG); + KEYWORD("llong", ARGTYPE_LLONG); + KEYWORD("ullong", ARGTYPE_ULLONG); KEYWORD("char", ARGTYPE_CHAR); KEYWORD("short", ARGTYPE_SHORT); KEYWORD("ushort", ARGTYPE_USHORT); @@ -912,6 +914,8 @@ parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num, case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_CHAR: case ARGTYPE_SHORT: case ARGTYPE_USHORT: diff --git a/sysdeps/linux-gnu/ia64/fetch.c b/sysdeps/linux-gnu/ia64/fetch.c index 54dc5b8..3828dd1 100644 --- a/sysdeps/linux-gnu/ia64/fetch.c +++ b/sysdeps/linux-gnu/ia64/fetch.c @@ -433,6 +433,8 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_POINTER: return allocate_arg(ctx, proc, info, valuep); @@ -467,6 +469,8 @@ arch_fetch_retval(struct fetch_context *ctx, enum tof type, case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_POINTER: case ARGTYPE_STRUCT: return allocate_ret(ctx, proc, info, valuep); diff --git a/sysdeps/linux-gnu/m68k/fetch.c b/sysdeps/linux-gnu/m68k/fetch.c index f6d8a0b..0870c82 100644 --- a/sysdeps/linux-gnu/m68k/fetch.c +++ b/sysdeps/linux-gnu/m68k/fetch.c @@ -170,6 +170,8 @@ arch_fetch_retval(struct fetch_context *context, enum tof type, case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_CHAR: case ARGTYPE_SHORT: case ARGTYPE_USHORT: diff --git a/sysdeps/linux-gnu/ppc/fetch.c b/sysdeps/linux-gnu/ppc/fetch.c index 9963a1e..6fa5b73 100644 --- a/sysdeps/linux-gnu/ppc/fetch.c +++ b/sysdeps/linux-gnu/ppc/fetch.c @@ -309,6 +309,8 @@ allocate_argument(struct fetch_context *ctx, struct Process *proc, case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_POINTER: break; diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c index 4357a1e..c1e9e2e 100644 --- a/sysdeps/linux-gnu/ppc/trace.c +++ b/sysdeps/linux-gnu/ppc/trace.c @@ -197,6 +197,10 @@ arch_type_sizeof(struct Process *proc, struct arg_type_info *info) case ARGTYPE_POINTER: return proc->e_machine == EM_PPC64 ? 8 : 4; + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: + return 8; + case ARGTYPE_FLOAT: return 4; case ARGTYPE_DOUBLE: @@ -233,6 +237,8 @@ arch_type_alignof(struct Process *proc, struct arg_type_info *info) case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_POINTER: case ARGTYPE_FLOAT: case ARGTYPE_DOUBLE: diff --git a/sysdeps/linux-gnu/s390/fetch.c b/sysdeps/linux-gnu/s390/fetch.c index fa8f42d..b01a71a 100644 --- a/sysdeps/linux-gnu/s390/fetch.c +++ b/sysdeps/linux-gnu/s390/fetch.c @@ -252,6 +252,8 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_CHAR: case ARGTYPE_SHORT: case ARGTYPE_USHORT: diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c index b9e05ff..6ded4fb 100644 --- a/sysdeps/linux-gnu/s390/trace.c +++ b/sysdeps/linux-gnu/s390/trace.c @@ -200,6 +200,10 @@ arch_type_sizeof(struct Process *proc, struct arg_type_info *info) case ARGTYPE_POINTER: return proc->e_class == ELFCLASS64 ? 8 : 4; + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: + return 8; + case ARGTYPE_FLOAT: return 4; case ARGTYPE_DOUBLE: @@ -240,6 +244,8 @@ arch_type_alignof(struct Process *proc, struct arg_type_info *info) case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_POINTER: return proc->e_class == ELFCLASS64 ? 8 : 4; diff --git a/sysdeps/linux-gnu/x86/fetch.c b/sysdeps/linux-gnu/x86/fetch.c index 4dab4cc..b9ed8f1 100644 --- a/sysdeps/linux-gnu/x86/fetch.c +++ b/sysdeps/linux-gnu/x86/fetch.c @@ -259,7 +259,13 @@ allocate_integer(struct fetch_context *context, struct value *valuep, HANDLE(0, rax); HANDLE(1, rdx); #else - HANDLE(0, eax); + case 0: // u64 return in EAX:EDX + copy_int_register(context, valuep, + context->iregs.eax, offset); + if(sz == 8) + copy_int_register(context, valuep, + context->iregs.edx, offset + 4); + return CLASS_INTEGER; #endif default: assert(!"Too many return value classes."); @@ -448,9 +454,10 @@ classify(struct Process *proc, struct fetch_context *context, case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_POINTER: - /* and LONGLONG */ /* CLASS_INTEGER */ classes[0] = CLASS_INTEGER; return 1; @@ -470,7 +477,7 @@ classify(struct Process *proc, struct fetch_context *context, * passed by value. */ assert(expr_is_compile_constant(info->u.array_info.length)); - long l; + long long l; if (expr_eval_constant(info->u.array_info.length, &l) < 0) return -1; @@ -609,6 +616,8 @@ arch_fetch_retval_32(struct fetch_context *context, enum tof type, case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_CHAR: case ARGTYPE_SHORT: case ARGTYPE_USHORT: diff --git a/sysdeps/linux-gnu/x86/trace.c b/sysdeps/linux-gnu/x86/trace.c index ed8bdb4..802bd0f 100644 --- a/sysdeps/linux-gnu/x86/trace.c +++ b/sysdeps/linux-gnu/x86/trace.c @@ -134,6 +134,10 @@ arch_type_sizeof(struct Process *proc, struct arg_type_info *info) case ARGTYPE_POINTER: return proc->e_machine == EM_X86_64 ? 8 : 4; + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: + return 8; + case ARGTYPE_FLOAT: return 4; case ARGTYPE_DOUBLE: @@ -175,6 +179,8 @@ arch_type_alignof(struct Process *proc, struct arg_type_info *info) case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_POINTER: return proc->e_machine == EM_X86_64 ? 8 : 4; diff --git a/type.c b/type.c index 3ce8563..0880e5b 100644 --- a/type.c +++ b/type.c @@ -43,6 +43,8 @@ type_get_simple(enum arg_type type) HANDLE(ARGTYPE_UINT) HANDLE(ARGTYPE_LONG) HANDLE(ARGTYPE_ULONG) + HANDLE(ARGTYPE_LLONG) + HANDLE(ARGTYPE_ULLONG) HANDLE(ARGTYPE_CHAR) HANDLE(ARGTYPE_SHORT) HANDLE(ARGTYPE_USHORT) @@ -239,6 +241,8 @@ type_destroy(struct arg_type_info *info) case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_CHAR: case ARGTYPE_SHORT: case ARGTYPE_USHORT: @@ -312,6 +316,10 @@ type_sizeof(struct Process *proc, struct arg_type_info *type) case ARGTYPE_ULONG: return sizeof(long); + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: + return sizeof(long long); + case ARGTYPE_FLOAT: return sizeof(float); @@ -328,7 +336,7 @@ type_sizeof(struct Process *proc, struct arg_type_info *type) case ARGTYPE_ARRAY: if (expr_is_compile_constant(type->u.array_info.length)) { - long l; + long long l; if (expr_eval_constant(type->u.array_info.length, &l) < 0) return -1; @@ -369,6 +377,7 @@ type_alignof(struct Process *proc, struct arg_type_info *type) struct { char c; short s; } cs; struct { char c; int i; } ci; struct { char c; long l; } cl; + struct { char c; long long ll; } cll; struct { char c; void* p; } cp; struct { char c; float f; } cf; struct { char c; double d; } cd; @@ -377,12 +386,16 @@ type_alignof(struct Process *proc, struct arg_type_info *type) static size_t short_alignment = alignof(s, cs); static size_t int_alignment = alignof(i, ci); static size_t long_alignment = alignof(l, cl); + static size_t llong_alignment = alignof(ll, cll); static size_t ptr_alignment = alignof(p, cp); static size_t float_alignment = alignof(f, cf); static size_t double_alignment = alignof(d, cd); switch (type->type) { size_t alignment; + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: + return llong_alignment; case ARGTYPE_LONG: case ARGTYPE_ULONG: return long_alignment; @@ -467,7 +480,7 @@ type_aggregate_size(struct arg_type_info *info) || info->type == ARGTYPE_ARRAY); switch (info->type) { - long ret; + long long ret; case ARGTYPE_ARRAY: if (expr_eval_constant(info->u.array_info.length, &ret) < 0) return (size_t)-1; @@ -489,6 +502,8 @@ type_is_integral(enum arg_type type) case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: + case ARGTYPE_LLONG: + case ARGTYPE_ULLONG: case ARGTYPE_CHAR: case ARGTYPE_SHORT: case ARGTYPE_USHORT: @@ -517,11 +532,13 @@ type_is_signed(enum arg_type type) case ARGTYPE_SHORT: case ARGTYPE_INT: case ARGTYPE_LONG: + case ARGTYPE_LLONG: return 1; + case ARGTYPE_USHORT: case ARGTYPE_UINT: case ARGTYPE_ULONG: - case ARGTYPE_USHORT: + case ARGTYPE_ULLONG: return 0; case ARGTYPE_VOID: @@ -551,8 +568,10 @@ type_get_fp_equivalent(struct arg_type_info *info) case ARGTYPE_SHORT: case ARGTYPE_INT: case ARGTYPE_LONG: + case ARGTYPE_LLONG: case ARGTYPE_UINT: case ARGTYPE_ULONG: + case ARGTYPE_ULLONG: case ARGTYPE_USHORT: case ARGTYPE_VOID: case ARGTYPE_ARRAY: diff --git a/type.h b/type.h index e8dec71..95838a0 100644 --- a/type.h +++ b/type.h @@ -32,6 +32,8 @@ enum arg_type { ARGTYPE_UINT, ARGTYPE_LONG, ARGTYPE_ULONG, + ARGTYPE_LLONG, + ARGTYPE_ULLONG, ARGTYPE_CHAR, ARGTYPE_SHORT, ARGTYPE_USHORT, diff --git a/value.c b/value.c index f89570f..3209b9f 100644 --- a/value.c +++ b/value.c @@ -219,8 +219,8 @@ value_size(struct value *val, struct value_dict *arguments) arguments, &length) < 0) return (size_t)-1; - size_t l; - int o = value_extract_word(&length, (long *)&l, arguments); + uint64_t l; + int o = value_extract_word(&length, (long long *)&l, arguments); value_destroy(&length); if (o < 0) @@ -278,7 +278,7 @@ value_init_deref(struct value *ret_val, struct value *valp) /* Note: extracting a pointer value should not need value_dict * with function arguments. */ - long l; + long long l; if (value_extract_word(valp, &l, NULL) < 0) return -1; @@ -368,7 +368,7 @@ value_extract_buf_sz(struct value *value, unsigned char *tgt, size_t sz, } int -value_extract_word(struct value *value, long *retp, +value_extract_word(struct value *value, long long *retp, struct value_dict *arguments) { size_t sz = type_sizeof(value->inferior, value->type); @@ -387,16 +387,16 @@ value_extract_word(struct value *value, long *retp, switch (sz) { case 1: - *retp = (long)u.u8; + *retp = (long long)u.u8; return 0; case 2: - *retp = (long)u.u16; + *retp = (long long)u.u16; return 0; case 4: - *retp = (long)u.u32; + *retp = (long long)u.u32; return 0; case 8: - *retp = (long)u.u64; + *retp = (long long)u.u64; return 0; default: assert(sz != sz); diff --git a/value.h b/value.h index 795573c..a527667 100644 --- a/value.h +++ b/value.h @@ -52,7 +52,7 @@ struct value { union { void *address; /* VAL_LOC_COPY, VAL_LOC_SHARED */ arch_addr_t inf_address; /* VAL_LOC_INFERIOR */ - long value; /* VAL_LOC_WORD */ + long long value; /* VAL_LOC_WORD */ unsigned char buf[0]; } u; enum value_location_t where; @@ -137,7 +137,7 @@ size_t value_size(struct value *val, struct value_dict *arguments); /* Extract at most word-sized datum from the value. Return 0 on * success or negative value on failure. */ -int value_extract_word(struct value *val, long *retp, +int value_extract_word(struct value *val, long long *retp, struct value_dict *arguments); /* Copy contents of VAL to DATA. The buffer must be large enough to diff --git a/zero.c b/zero.c index bc119ee..ddb5e0d 100644 --- a/zero.c +++ b/zero.c @@ -58,7 +58,7 @@ static int zero_callback(struct value *ret_value, struct value *lhs, struct value *rhs, struct value_dict *arguments, void *data) { - long l; + long long l; if (value_extract_word(rhs, &l, arguments) < 0) return -1; if (l < 0)
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Wed, 26 Jul 2023 01:42:23 +0200 Subject: ltrace.conf: openat*(), getopt_long*() consistency, fseek*(), ftell*(), reallocarray(), pread*(), pwrite*(), lseek*() with whence enum, *readv*(), *writev*(), __cxa_finalize() Of course, this assumes that symbols ending in 64 are the only ones taking a 64-bit off_t, and thus the ones that don't take longs. This holds on x86, but probably not any new arches which just don't have the weird broken *64() symbols. --- etc/ltrace.conf | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/etc/ltrace.conf b/etc/ltrace.conf index 8f83986..855d373 100644 --- a/etc/ltrace.conf +++ b/etc/ltrace.conf @@ -55,15 +55,18 @@ int dlclose(addr); addr __errno_location(); ; fcntl.h -int open(string,int,octal); ; WARNING: 3rd argument may not be there -int open64(string,int,octal); ; WARNING: 3rd argument may not be there +typedef openat_fd = enum(AT_FDCWD=-100); +int open(string,hex(int),octal); ; WARNING: 3rd argument may not be there +int open64(string,hex(int),octal); ; WARNING: 3rd argument may not be there +int openat(openat_fd,string,hex(int),octal); ; WARNING: 4th argument may not be there +int openat64(openat_fd,string,hex(int),octal); ; WARNING: 4th argument may not be there ; fnmatch.h int fnmatch(string, string, int); ; getopt.h -int getopt_long(int,addr,string,addr,int*); -int getopt_long_only(int,addr,string,addr,addr); +int getopt_long(int,addr,string,addr,+int*); +int getopt_long_only(int,addr,string,addr,+int*); ; grp.h void endgrent(); @@ -194,6 +197,13 @@ ulong fread(addr,ulong,ulong,file); ulong fread_unlocked(addr,ulong,ulong,file); ulong fwrite(string,ulong,ulong,file); ulong fwrite_unlocked(string,ulong,ulong,file); +typedef lseek_whence = enum(SEEK_SET=0, SEEK_CUR=1, SEEK_END=2, SEEK_DATA=3, SEEK_HOLE=4); +int fseek(file,long,lseek_whence); +int fseeko(file,long,lseek_whence); +int fseeko64(file,llong,lseek_whence); +long ftell(file); +long ftello(file); +llong ftello64(file); int pclose(addr); void perror(string); addr popen(string, string); @@ -227,6 +237,7 @@ addr malloc(ulong); void qsort(addr,ulong,ulong,addr); int random(); addr realloc(addr,ulong); +addr reallocarray(addr,ulong,ulong); void srandom(uint); int system(string); @@ -335,6 +346,8 @@ int isatty(int); int link(string,string); int mkdir(string,octal); long read(int, +string[retval], ulong); +long pread(int,+string[retval],ulong,long); +long pread64(int,+string[retval],ulong,llong); int rmdir(string); int seteuid(uint); int setgid(int); @@ -350,11 +363,27 @@ string ttyname(int); int unlink(string); void usleep(uint); long write(int, string3, ulong); +long pwrite(int,string3,ulong,long); +long pwrite64(int,string3,ulong,llong); addr sbrk(long); int getpagesize(); -long lseek(int,long,int); +long lseek(int,long,lseek_whence); +long lseek64(int,llong,lseek_whence); int pipe(addr); +; sys/uio.h +typedef iovec = struct(string[elt2],ulong); +long readv(int,+array(iovec,arg3),int); +long writev(int,array(iovec,arg3),int); +long preadv(int,+array(iovec,arg3),int,long); +long pwritev(int,array(iovec,arg3),int,long); +long preadv64(int,+array(iovec,arg3),int,llong); +long pwritev64(int,array(iovec,arg3),int,llong); +long preadv2(int,+array(iovec,arg3),int,long,hex(int)); +long pwritev2(int,array(iovec,arg3),int,long,hex(int)); +long preadv64v2(int,+array(iovec,arg3),int,llong,hex(int)); +long pwritev64v2(int,array(iovec,arg3),int,llong,hex(int)); + ; utmp.h void endutent(); addr getutent(); @@ -414,6 +443,9 @@ addr acl_from_mode(octal); int acl_get_perm(addr,uint); string acl_to_any_text(addr,string,char,int); +; cxxabi.h +void __cxa_finalize(addr); + ; other symbols not included above long a64l(string); string l64a(long);
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Wed, 26 Jul 2023 02:55:43 +0200 Subject: Remove extraneous whitespace around [] and {} Compare pread64(3, "14044\n14045\n14046\n14047\n14048\n14"..., 65536, 73152) = 65536 memmove(0x5769c1a4, "6\n", 2) = 0x5769c1a4 memcpy(0x5769c1a0, "2496", 4) = 0x5769c1a0 writev(1, [ { "24966\n", 6 }, { "24965\n", 6 }, { "24964\n", 6 }, { "24963\n", 6 }... ], 1024) = 6144 writev(1, [ { "23942\n", 6 }, { "23941\n", 6 }, { "23940\n", 6 }, { "23939\n", 6 }... ], 1024) = 6144 writev(1, [ { "22918\n", 6 }, { "22917\n", 6 }, { "22916\n", 6 }, { "22915\n", 6 }... ], 1024) = 6144 writev(1, [ { "21894\n", 6 }, { "21893\n", 6 }, { "21892\n", 6 }, { "21891\n", 6 }... ], 1024) = 6144 writev(1, [ { "20870\n", 6 }, { "20869\n", 6 }, { "20868\n", 6 }, { "20867\n", 6 }... ], 1024) = 6144 writev(1, [ { "19846\n", 6 }, { "19845\n", 6 }, { "19844\n", 6 }, { "19843\n", 6 }... ], 1024) = 6144 writev(1, [ { "18822\n", 6 }, { "18821\n", 6 }, { "18820\n", 6 }, { "18819\n", 6 }... ], 1024) = 6144 writev(1, [ { "17798\n", 6 }, { "17797\n", 6 }, { "17796\n", 6 }, { "17795\n", 6 }... ], 1024) = 6144 writev(1, [ { "16774\n", 6 }, { "16773\n", 6 }, { "16772\n", 6 }, { "16771\n", 6 }... ], 1024) = 6144 writev(1, [ { "15750\n", 6 }, { "15749\n", 6 }, { "15748\n", 6 }, { "15747\n", 6 }... ], 1024) = 6144 writev(1, [ { "14726\n", 6 }, { "14725\n", 6 }, { "14724\n", 6 }, { "14723\n", 6 }... ], 682) = 4092 memcpy(0x5769c1a0, "14044\n", 6) = 0x5769c1a0 pread64(3, "5\n1746\n1747\n1748\n1749\n1750\n1751\n"..., 65536, 7616) = 65536 writev(1, [ { "14044\n", 6 }, { "14043\n", 6 }, { "14042\n", 6 }, { "14041\n", 6 }... ], 1024) = 6144 writev(1, [ { "13020\n", 6 }, { "13019\n", 6 }, { "13018\n", 6 }, { "13017\n", 6 }... ], 1024) = 6144 writev(1, [ { "11996\n", 6 }, { "11995\n", 6 }, { "11994\n", 6 }, { "11993\n", 6 }... ], 1024) = 6144 writev(1, [ { "10972\n", 6 }, { "10971\n", 6 }, { "10970\n", 6 }, { "10969\n", 6 }... ], 1024) = 6093 writev(1, [ { "9948\n", 5 }, { "9947\n", 5 }, { "9946\n", 5 }, { "9945\n", 5 }... ], 1024) = 5120 writev(1, [ { "8924\n", 5 }, { "8923\n", 5 }, { "8922\n", 5 }, { "8921\n", 5 }... ], 1024) = 5120 writev(1, [ { "7900\n", 5 }, { "7899\n", 5 }, { "7898\n", 5 }, { "7897\n", 5 }... ], 1024) = 5120 writev(1, [ { "6876\n", 5 }, { "6875\n", 5 }, { "6874\n", 5 }, { "6873\n", 5 }... ], 1024) = 5120 writev(1, [ { "5852\n", 5 }, { "5851\n", 5 }, { "5850\n", 5 }, { "5849\n", 5 }... ], 1024) = 5120 writev(1, [ { "4828\n", 5 }, { "4827\n", 5 }, { "4826\n", 5 }, { "4825\n", 5 }... ], 1024) = 5120 writev(1, [ { "3804\n", 5 }, { "3803\n", 5 }, { "3802\n", 5 }, { "3801\n", 5 }... ], 1024) = 5120 writev(1, [ { "2780\n", 5 }, { "2779\n", 5 }, { "2778\n", 5 }, { "2777\n", 5 }... ], 1024) = 5120 writev(1, [ { "1756\n", 5 }, { "1755\n", 5 }, { "1754\n", 5 }, { "1753\n", 5 }... ], 11) = 55 memcpy(0x5769c1a0, "5\n", 2) = 0x5769c1a0 pread64(3, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"..., 7616, 0) = 7616 memmove(0x5769c1a3, "5\n", 2) = 0x5769c1a3 memcpy(0x5769c1a0, "174", 3) = 0x5769c1a0 writev(1, [ { "1745\n", 5 }, { "1744\n", 5 }, { "1743\n", 5 }, { "1742\n", 5 }... ], 1024) = 4842 writev(1, [ { "721\n", 4 }, { "720\n", 4 }, { "719\n", 4 }, { "718\n", 4 }... ], 720) = 2774 memcpy(0x5769c1a0, "1\n", 2) = 0x5769c1a0 writev(1, [ { "1\n", 2 } ], 1) = 2 close(3) = 0 To pread64(3, "14044\n14045\n14046\n14047\n14048\n14"..., 65536, 73152) = 65536 memmove(0x574c41a4, "6\n", 2) = 0x574c41a4 memcpy(0x574c41a0, "2496", 4) = 0x574c41a0 writev(1, [{"24966\n", 6}, {"24965\n", 6}, {"24964\n", 6}, {"24963\n", 6}...], 1024) = 6144 writev(1, [{"23942\n", 6}, {"23941\n", 6}, {"23940\n", 6}, {"23939\n", 6}...], 1024) = 6144 writev(1, [{"22918\n", 6}, {"22917\n", 6}, {"22916\n", 6}, {"22915\n", 6}...], 1024) = 6144 writev(1, [{"21894\n", 6}, {"21893\n", 6}, {"21892\n", 6}, {"21891\n", 6}...], 1024) = 6144 writev(1, [{"20870\n", 6}, {"20869\n", 6}, {"20868\n", 6}, {"20867\n", 6}...], 1024) = 6144 writev(1, [{"19846\n", 6}, {"19845\n", 6}, {"19844\n", 6}, {"19843\n", 6}...], 1024) = 6144 writev(1, [{"18822\n", 6}, {"18821\n", 6}, {"18820\n", 6}, {"18819\n", 6}...], 1024) = 6144 writev(1, [{"17798\n", 6}, {"17797\n", 6}, {"17796\n", 6}, {"17795\n", 6}...], 1024) = 6144 writev(1, [{"16774\n", 6}, {"16773\n", 6}, {"16772\n", 6}, {"16771\n", 6}...], 1024) = 6144 writev(1, [{"15750\n", 6}, {"15749\n", 6}, {"15748\n", 6}, {"15747\n", 6}...], 1024) = 6144 writev(1, [{"14726\n", 6}, {"14725\n", 6}, {"14724\n", 6}, {"14723\n", 6}...], 682) = 4092 memcpy(0x574c41a0, "14044\n", 6) = 0x574c41a0 pread64(3, "5\n1746\n1747\n1748\n1749\n1750\n1751\n"..., 65536, 7616) = 65536 writev(1, [{"14044\n", 6}, {"14043\n", 6}, {"14042\n", 6}, {"14041\n", 6}...], 1024) = 6144 writev(1, [{"13020\n", 6}, {"13019\n", 6}, {"13018\n", 6}, {"13017\n", 6}...], 1024) = 6144 writev(1, [{"11996\n", 6}, {"11995\n", 6}, {"11994\n", 6}, {"11993\n", 6}...], 1024) = 6144 writev(1, [{"10972\n", 6}, {"10971\n", 6}, {"10970\n", 6}, {"10969\n", 6}...], 1024) = 6093 writev(1, [{"9948\n", 5}, {"9947\n", 5}, {"9946\n", 5}, {"9945\n", 5}...], 1024) = 5120 writev(1, [{"8924\n", 5}, {"8923\n", 5}, {"8922\n", 5}, {"8921\n", 5}...], 1024) = 5120 writev(1, [{"7900\n", 5}, {"7899\n", 5}, {"7898\n", 5}, {"7897\n", 5}...], 1024) = 5120 writev(1, [{"6876\n", 5}, {"6875\n", 5}, {"6874\n", 5}, {"6873\n", 5}...], 1024) = 5120 writev(1, [{"5852\n", 5}, {"5851\n", 5}, {"5850\n", 5}, {"5849\n", 5}...], 1024) = 5120 writev(1, [{"4828\n", 5}, {"4827\n", 5}, {"4826\n", 5}, {"4825\n", 5}...], 1024) = 5120 writev(1, [{"3804\n", 5}, {"3803\n", 5}, {"3802\n", 5}, {"3801\n", 5}...], 1024) = 5120 writev(1, [{"2780\n", 5}, {"2779\n", 5}, {"2778\n", 5}, {"2777\n", 5}...], 1024) = 5120 writev(1, [{"1756\n", 5}, {"1755\n", 5}, {"1754\n", 5}, {"1753\n", 5}...], 11) = 55 memcpy(0x574c41a0, "5\n", 2) = 0x574c41a0 pread64(3, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"..., 7616, 0) = 7616 memmove(0x574c41a3, "5\n", 2) = 0x574c41a3 memcpy(0x574c41a0, "174", 3) = 0x574c41a0 writev(1, [{"1745\n", 5}, {"1744\n", 5}, {"1743\n", 5}, {"1742\n", 5}...], 1024) = 4842 writev(1, [{"721\n", 4}, {"720\n", 4}, {"719\n", 4}, {"718\n", 4}...], 720) = 2774 memcpy(0x574c41a0, "1\n", 2) = 0x574c41a0 writev(1, [{"1\n", 2}], 1) = 2 close(3) = 0 --- lens_default.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lens_default.c b/lens_default.c index 2fd4915..71d9584 100644 --- a/lens_default.c +++ b/lens_default.c @@ -231,7 +231,7 @@ static int format_struct(FILE *stream, struct value *value, struct value_dict *arguments) { int written = 0; - if (acc_fprintf(&written, stream, "{ ") < 0) + if (acc_fprintf(&written, stream, "{") < 0) return -1; int need_delim = 0; @@ -250,7 +250,7 @@ format_struct(FILE *stream, struct value *value, struct value_dict *arguments) written += o; } - if (acc_fprintf(&written, stream, " }") < 0) + if (acc_fprintf(&written, stream, "}") < 0) return -1; return written; } @@ -425,7 +425,7 @@ toplevel_format_lens(struct lens *lens, FILE *stream, case ARGTYPE_ARRAY: return format_array(stream, value, arguments, value->type->u.array_info.length, - options.arraylen, 1, "[ ", " ]", ", "); + options.arraylen, 1, "[", "]", ", "); } abort(); }
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Wed, 26 Jul 2023 19:12:16 +0200 Subject: Read larger-than-word buffers with process_vm_readv. Pre-buffer arrays of primitives The first optimisation is obvious: at one point, without it, ltrace does ptrace(PTRACE_PEEKDATA) /79959 consecutive times/ (thus reading 640kB; imagine how much slower it'd be on i686 with double the syscall count and slower syscalls). The second is necessary because arrays are reified by reifying each element separately. Thus, a 64kB string will cause ltrace to PTRACE_PEEKDATA 65`536 times.) Instead, cooperatively notify the reification module before formatting an array, and read (up to a megabyte) into a static buffer. This means that the no-flag and -s9999999999 runs complete without ever PTRACE_PEEKDATAing a singular character. The zero() lens would (needlessly) check more than the required mapping size, which is limited by -A and -s: restrict it to never try more than the maximum of those + 1, which allows us to speculatively pre-map the entire prospective NUL-terminated string too. Evaluation: The program consists almost exclusively of pread64(64kB) of seven-byte-lines and writev(1024)s (where each iov is one line) of the whole read buffer. The input file is 6.6M, of seq 1000000. $ time out/cmd/tail -r /tmp/1000000 > /dev/null real 0m0.030s user 0m0.017s sys 0m0.013s 165kB of output: $ time ltrace -o /dev/null -F /etc/ltrace.conf out/cmd/tail -r /tmp/1000000 > /dev/null real 0m8.508s user 0m2.081s sys 0m6.389s $ time ~/backports/ltrace/ltrace -o /dev/null -F /etc/ltrace.conf out/cmd/tail -r /tmp/1000000 > /dev/null real 0m0.464s user 0m0.173s sys 0m0.282s 7.7MB of output: $ time ltrace -o /dev/null -F /etc/ltrace.conf -s9999999999 out/cmd/tail -r /tmp/1000000 > /dev/null real 0m17.820s user 0m5.384s sys 0m12.395s $ time ~/backports/ltrace/ltrace -o /dev/null -F /etc/ltrace.conf -s9999999999 out/cmd/tail -r /tmp/1000000 > /dev/null real 0m2.678s user 0m2.377s sys 0m0.265s 24M of output (especially torturous for the reasons mentioned above): $ time ltrace -o /dev/null -F /etc/ltrace.conf -{A,s}9999999999 out/cmd/tail -r /tmp/1000000 > /dev/null real 0m37.834s user 0m12.685s sys 0m25.027s $ time ~/backports/ltrace/ltrace -o /dev/null -F /etc/ltrace.conf -{A,s}9999999999 out/cmd/tail -r /tmp/1000000 > /dev/null real 0m19.512s user 0m8.392s sys 0m10.997s --- expr.c | 6 ++++++ expr.h | 3 +++ lens_default.c | 10 +++++++++- sysdeps/linux-gnu/trace.c | 29 ++++++++++++++++++++++------- value.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ value.h | 8 ++++++++ zero.c | 7 +++++++ 7 files changed, 102 insertions(+), 8 deletions(-) diff --git a/expr.c b/expr.c index 01ce4c6..e0215d5 100644 --- a/expr.c +++ b/expr.c @@ -178,6 +178,12 @@ expr_is_compile_constant(struct expr_node *node) return node->kind == EXPR_OP_CONST; } +int +expr_is_trivial(struct expr_node *node) +{ + return !(node->kind == EXPR_OP_CALL1 || node->kind == EXPR_OP_CALL2); +} + static int eval_up(struct expr_node *node, struct value *context, struct value_dict *arguments, struct value *ret_value) diff --git a/expr.h b/expr.h index aa4b0b6..dfd6789 100644 --- a/expr.h +++ b/expr.h @@ -150,6 +150,9 @@ int expr_eval_word(struct expr_node *node, struct value *context, * things like sizeof or simple expressions might be allowed. */ int expr_is_compile_constant(struct expr_node *node); +/* Nonzero if evalutaion won't cause side effects (i.e. not EXPR_OP_CALL*). */ +int expr_is_trivial(struct expr_node *node); + /* Returns a pre-computed expression "self". */ struct expr_node *expr_self(void); diff --git a/lens_default.c b/lens_default.c index 71d9584..767465f 100644 --- a/lens_default.c +++ b/lens_default.c @@ -342,14 +342,22 @@ format_array(FILE *stream, struct value *value, struct value_dict *arguments, struct expr_node *length, size_t maxlen, int before, const char *open, const char *close, const char *delim) { - /* We need "long" to be long enough to cover the whole address + /* We need "long long" to be long enough to cover the whole address * space. */ (void)sizeof(char[1 - 2*(sizeof(long long) < sizeof(void *))]); + + int preloaded __attribute__((__cleanup__(value_preload_for_array_flush))) = -1; + if(!expr_is_trivial(length)) // zero() + preloaded = value_preload_for_array(value, maxlen + 1); + long long l; if (expr_eval_word(length, value, arguments, &l) < 0) return -1; size_t len = (size_t)l; + if(preloaded == -1) + preloaded = value_preload_for_array(value, len > maxlen ? maxlen : len); + int written = 0; if (acc_fprintf(&written, stream, "%s", open) < 0) return -1; diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c index e13b761..8fb0fcd 100644 --- a/sysdeps/linux-gnu/trace.c +++ b/sysdeps/linux-gnu/trace.c @@ -21,6 +21,7 @@ * 02110-1301 USA */ +#define _GNU_SOURCE #include "config.h" #include <asm/unistd.h> @@ -31,6 +32,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/uio.h> #include <unistd.h> #ifdef HAVE_LIBSELINUX @@ -1183,8 +1185,21 @@ umovebytes(Process *proc, void *addr, void *laddr, size_t len) { int started = 0; size_t offset = 0, bytes_read = 0; + + if(len > sizeof(long)) { + struct iovec local = {.iov_base = laddr, .iov_len = len}; + struct iovec remote = {.iov_base = addr, .iov_len = len}; + ssize_t rd = process_vm_readv(proc->pid, &local, 1, &remote, 1, 0); + if(rd != -1) { + started = 1; + offset = bytes_read = rd; + } + } + while (offset < len) { - a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset, 0); + int misalignment = (uintptr_t)(addr + offset) % sizeof(long); + + a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset - misalignment, 0); if (a.a == -1 && errno) { if (started && errno == EIO) return bytes_read; @@ -1193,15 +1208,15 @@ umovebytes(Process *proc, void *addr, void *laddr, size_t len) { } started = 1; - if (len - offset >= sizeof(long)) { - memcpy(laddr + offset, &a.c[0], sizeof(long)); - bytes_read += sizeof(long); + if (len - offset >= sizeof(long) - misalignment) { + memcpy(laddr + offset + misalignment, &a.c[0] + misalignment, sizeof(long) - misalignment); + bytes_read += sizeof(long) - misalignment; } else { - memcpy(laddr + offset, &a.c[0], len - offset); - bytes_read += (len - offset); + memcpy(laddr + offset + misalignment, &a.c[0] + misalignment, len - offset - misalignment); + bytes_read += (len - offset) - misalignment; } - offset += sizeof(long); + offset += sizeof(long) - misalignment; } return bytes_read; diff --git a/value.c b/value.c index 3209b9f..1edb0e4 100644 --- a/value.c +++ b/value.c @@ -122,6 +122,47 @@ value_in_inferior(struct value *valp, arch_addr_t address) valp->u.address = address; } +static char preload_buf[1024 * 1024]; +static arch_addr_t preload_buf_start, preload_buf_end; +int +value_preload_for_array(struct value *val, size_t len) +{ + if (preload_buf_start || val->where != VAL_LOC_INFERIOR) + return 0; + + struct arg_type_info *e_info = type_element(val->type, 0); + if (e_info == NULL) + return 0; + // We only have one preload: use it to map in arrays of primitives (strings. it's strings.) + switch(e_info->type) { + case ARGTYPE_VOID: + case ARGTYPE_ARRAY: + case ARGTYPE_STRUCT: + return 0; + default: + break; + } + + size_t el_sz = type_sizeof(val->inferior, e_info); + if (el_sz == (size_t)-1) + return 0; + + if (__builtin_mul_overflow(len, el_sz, &len) || len > sizeof(preload_buf)) + len = sizeof(preload_buf); + size_t rd = umovebytes(val->inferior, val->u.inf_address, preload_buf, len); + if(rd == (size_t)-1) + return 0; + preload_buf_start = val->u.inf_address; + preload_buf_end = val->u.inf_address + rd; + return 1; +} +void +value_preload_for_array_flush(int *preloaded) +{ + if (*preloaded && *preloaded != -1) + preload_buf_start = preload_buf_end = 0; +} + int value_reify(struct value *val, struct value_dict *arguments) { @@ -145,12 +186,18 @@ value_reify(struct value *val, struct value_dict *arguments) nloc = VAL_LOC_COPY; } + if (val->u.inf_address >= preload_buf_start && val->u.inf_address + size <= preload_buf_end) { + memcpy(data, preload_buf + (val->u.inf_address - preload_buf_start), size); + goto ok; + } + if (umovebytes(val->inferior, val->u.inf_address, data, size) < size) { if (nloc == VAL_LOC_COPY) free(data); return -1; } +ok: val->where = nloc; if (nloc == VAL_LOC_COPY) val->u.address = data; diff --git a/value.h b/value.h index a527667..e6edabd 100644 --- a/value.h +++ b/value.h @@ -113,6 +113,14 @@ int value_init_element(struct value *ret_val, struct value *valp, size_t element * RET_VAL. Returns 0 on success, or negative value on failure. */ int value_init_deref(struct value *ret_val, struct value *valp); +/* Pre-read the contents of an array of max length len from the tracee. + * Optional optimisation. */ +int value_preload_for_array(struct value *val, size_t len); + +/* Must point to return value from value_preload_for_array() or -1. + * Suitable for use as __attribute__((__cleanup__(value_preload_for_array_flush))). */ +void value_preload_for_array_flush(int *preloaded); + /* If value is in inferior, copy it over to ltrace. Return 0 for * success or negative value for failure. */ int value_reify(struct value *val, struct value_dict *arguments); diff --git a/zero.c b/zero.c index ddb5e0d..a249086 100644 --- a/zero.c +++ b/zero.c @@ -32,6 +32,13 @@ zero_callback_max(struct value *ret_value, struct value *lhs, struct value_dict *arguments, size_t max, void *data) { + static size_t maxlen; + if(!maxlen) { + maxlen = options.strlen > options.arraylen ? options.strlen : options.arraylen; + if(maxlen != (size_t)-1) + ++maxlen; + } + max = max < maxlen ? max : maxlen; size_t i; for (i = 0; i < max; ++i) { struct value element;
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Thu, 27 Jul 2023 05:40:14 +0200 Subject: fix misalignment handling. idk why i wrote it like that but it breaks printf --- sysdeps/linux-gnu/trace.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c index 8fb0fcd..33ebdf2 100644 --- a/sysdeps/linux-gnu/trace.c +++ b/sysdeps/linux-gnu/trace.c @@ -1209,12 +1209,12 @@ umovebytes(Process *proc, void *addr, void *laddr, size_t len) { started = 1; if (len - offset >= sizeof(long) - misalignment) { - memcpy(laddr + offset + misalignment, &a.c[0] + misalignment, sizeof(long) - misalignment); + memcpy(laddr + offset, &a.c[0] + misalignment, sizeof(long) - misalignment); bytes_read += sizeof(long) - misalignment; } else { - memcpy(laddr + offset + misalignment, &a.c[0] + misalignment, len - offset - misalignment); - bytes_read += (len - offset) - misalignment; + memcpy(laddr + offset, &a.c[0] + misalignment, len - offset); + bytes_read += len - offset; } offset += sizeof(long) - misalignment; }
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Thu, 27 Jul 2023 05:59:19 +0200 Subject: Handle format %b. Add bin() lens --- lens_default.c | 21 +++++++++++++++++++++ lens_default.h | 3 +++ ltrace.conf.5 | 3 +++ printf.c | 4 ++++ read_config_file.c | 1 + 5 files changed, 32 insertions(+) diff --git a/lens_default.c b/lens_default.c index 767465f..e381b50 100644 --- a/lens_default.c +++ b/lens_default.c @@ -56,6 +56,12 @@ READER(read_double, double) #undef READER +#ifndef PRIb64 // Underlying format supported by bookworm glibc, but macros not exposed until 2.38 +#ifndef __PRI64_PREFIX +#define __PRI64_PREFIX "ll" +#endif +#define PRIb64 __PRI64_PREFIX "b" +#endif #define HANDLE_WIDTH(BITS) \ do { \ long long l; \ @@ -75,6 +81,8 @@ READER(read_double, double) return fprintf(stream, "%"PRIu64, v); \ case INT_FMT_o: \ return fprintf(stream, "0%"PRIo64, v); \ + case INT_FMT_b: \ + return fprintf(stream, "%#"PRIb64, v); \ } \ } while (0) @@ -84,6 +92,7 @@ enum int_fmt_t INT_FMT_u, INT_FMT_o, INT_FMT_x, + INT_FMT_b, INT_FMT_unknown, INT_FMT_default, }; @@ -463,6 +472,18 @@ struct lens blind_lens = { }; +static int +binary_lens_format_cb(struct lens *lens, FILE *stream, + struct value *value, struct value_dict *arguments) +{ + return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_b); +} + +struct lens binary_lens = { + .format_cb = binary_lens_format_cb, +}; + + static int octal_lens_format_cb(struct lens *lens, FILE *stream, struct value *value, struct value_dict *arguments) diff --git a/lens_default.h b/lens_default.h index 1206ffc..66fee66 100644 --- a/lens_default.h +++ b/lens_default.h @@ -29,6 +29,9 @@ extern struct lens default_lens; /* A lens that doesn't output anything. */ extern struct lens blind_lens; +/* A lens that formats integers in binary. */ +extern struct lens binary_lens; + /* A lens that formats integers in octal. */ extern struct lens octal_lens; diff --git a/ltrace.conf.5 b/ltrace.conf.5 index 957fe8b..01a2132 100644 --- a/ltrace.conf.5 +++ b/ltrace.conf.5 @@ -122,6 +122,9 @@ .SH LENSES Ltrace understands the following lenses: .TP +.B bin(\fITYPE\fB) +The argument, which should be an integer type, is formatted in base-2. + .B oct(\fITYPE\fB) The argument, which should be an integer type, is formatted in base-8. diff --git a/printf.c b/printf.c index 773d104..860e445 100644 --- a/printf.c +++ b/printf.c @@ -264,6 +264,10 @@ param_printf_next(struct param_enum *self, struct arg_type_info *infop, self->percent = 0; break; + case 'b': + lens = &binary_lens; + goto uint; + case 'o': lens = &octal_lens; goto uint; diff --git a/read_config_file.c b/read_config_file.c index 98278ac..6fa3c21 100644 --- a/read_config_file.c +++ b/read_config_file.c @@ -963,6 +963,7 @@ static struct named_lens { struct lens *lens; } lenses[] = { { "hide", &blind_lens }, + { "bin", &binary_lens }, { "octal", &octal_lens }, { "oct", &octal_lens }, { "bitvec", &bitvect_lens },
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Thu, 27 Jul 2023 15:34:25 +0200 Subject: Add %w[f]{WIDTH}x (glibc 2.38, C2x) This even lets you trace programs that your libc doesn't have support for! (By accident.) $ cat qwe.c int main() { printf("%#b\n", 69); printf("%w64x %s\n", "gameing", (char *)0); write(1, "zupa\n", 5); } $ ./ltrace -F ./ltrace.conf ./qwe | tail printf("%#b\n", 0b1000101) = 10 printf("%w64x %s\n", 0x563ab86eb013, nil) = 14 write(1, "zupa\n", 0b101) = 5 +++ exited (status 0) +++ zupa 0b1000101 %w64x gameing --- printf.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/printf.c b/printf.c index 860e445..a9b90cd 100644 --- a/printf.c +++ b/printf.c @@ -23,6 +23,7 @@ #include <assert.h> #include <stdlib.h> +#include <stdint.h> #include "printf.h" #include "type.h" @@ -188,6 +189,7 @@ param_printf_next(struct param_enum *self, struct arg_type_info *infop, char len_buf[25] = {}; size_t len_buf_len = 0; struct lens *lens = NULL; + _Bool digital_width = 0, digital_width_fast = 0; for (; self->ptr < self->end; ++self->ptr) { if (!self->percent) { @@ -223,11 +225,49 @@ param_printf_next(struct param_enum *self, struct arg_type_info *infop, case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - /* Field length likewise, but we need to parse - * this to attach the appropriate string - * length expression. */ - if (len_buf_len < sizeof(len_buf) - 1) - len_buf[len_buf_len++] = *self->ptr; + if(!digital_width) { + /* Field length likewise, but we need to parse + * this to attach the appropriate string + * length expression. */ + if (len_buf_len < sizeof(len_buf) - 1) + len_buf[len_buf_len++] = *self->ptr; + } else { + /* %w32d */ + digital_width = 0; + int b; + switch(strtoul(self->ptr, (char **)&self->ptr, 10)) { +#define W_OR_WF(bits) \ + case bits: \ + --self->ptr; \ + b = digital_width_fast ? sizeof(int_fast##bits##_t) \ + : sizeof(int##bits##_t); \ + break + W_OR_WF(8); + W_OR_WF(16); + W_OR_WF(32); + W_OR_WF(64); + default: + goto err; + } + switch(b) { + case 1: + hlf = 2; + lng = 0; + break; + case 2: + hlf = 1; + lng = 0; + break; + case 4: + hlf = 0; + lng = 0; + break; + case 8: + hlf = 0; + lng = 2; + break; + } + } continue; case 'h': @@ -258,6 +298,16 @@ param_printf_next(struct param_enum *self, struct arg_type_info *infop, lng = 1; /* XXX ABI should tell */ continue; + case 'w': + ++self->ptr; + digital_width = 1; + if(self->ptr >= self->end || *self->ptr != 'f') { + --self->ptr; + digital_width_fast = 0; + } else + digital_width_fast = 1; + continue; + case 'd': case 'i': format_type = ARGTYPE_INT; @@ -339,6 +389,7 @@ param_printf_next(struct param_enum *self, struct arg_type_info *infop, return 0; } +err: *infop = *type_get_simple(ARGTYPE_VOID); return 0; }
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Tue, 1 Aug 2023 17:09:11 +0200 Subject: Add splice()/copy_file_range()/sendfile[64]() Before: splice(3, 0x7ffd4b66f348, 1, 0) = -1 copy_file_range(3, 0x7ffd772bbf28, 1, 0) = -1 sendfile64(1, 3, 0x7ffd772bbf28, 0x400000) = -1 pread64(3 <no return ...> error: maximum array length seems negative , "", 65536, -65536) = -1 After: splice(3, -65536, 1, nil, 4194304, 0b101) = -1 copy_file_range(3, -65536, 1, nil, 4194304, 0) = -1 sendfile64(1, 3, -65536, 4194304) = -1 pread64(3 <no return ...> error: maximum array length seems negative , "", 65536, -65536) = -1 --- etc/ltrace.conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/etc/ltrace.conf b/etc/ltrace.conf index 855d373..db914cb 100644 --- a/etc/ltrace.conf +++ b/etc/ltrace.conf @@ -60,6 +60,12 @@ int open(string,hex(int),octal); ; WARNING: 3rd argument may not be there int open64(string,hex(int),octal); ; WARNING: 3rd argument may not be there int openat(openat_fd,string,hex(int),octal); ; WARNING: 4th argument may not be there int openat64(openat_fd,string,hex(int),octal); ; WARNING: 4th argument may not be there +long splice(int,llong*,int,llong*,ulong,bin(uint)) +; unistd.h +long copy_file_range(int,llong*,int,llong*,ulong,bin(uint)) +; sys/sendfile.h +long sendfile(int,int,long*,ulong) +long sendfile64(int,int,llong*,ulong) ; fnmatch.h int fnmatch(string, string, int);
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Tue, 1 Aug 2023 17:30:15 +0200 Subject: __errno_location() returns an int*, not addr This means that before: __errno_location() = 0x7f2707f256c0 __errno_location() = 0x7f2707f256c0 __errno_location() = 0x7f2707f256c0 After: __errno_location() = 0 __errno_location() = 22 __errno_location() = 22 And this __errno_location() is made useful --- etc/ltrace.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/ltrace.conf b/etc/ltrace.conf index db914cb..3d4f5cd 100644 --- a/etc/ltrace.conf +++ b/etc/ltrace.conf @@ -52,7 +52,7 @@ addr dlsym(addr, string); int dlclose(addr); ; errno.h -addr __errno_location(); +int* __errno_location(); ; fcntl.h typedef openat_fd = enum(AT_FDCWD=-100);
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Thu, 3 Aug 2023 16:47:06 +0200 Subject: Fix config lines being truncated to 1kB --- read_config_file.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/read_config_file.c b/read_config_file.c index 6fa3c21..561351d 100644 --- a/read_config_file.c +++ b/read_config_file.c @@ -1278,7 +1278,8 @@ init_global_config(void) void read_config_file(char *file) { FILE *stream; - char buf[1024]; + char *line = NULL; + size_t linecap = 0; filename = file; stream = fopen(filename, "r"); @@ -1289,10 +1290,10 @@ read_config_file(char *file) { debug(1, "Reading config file `%s'...", filename); line_no = 0; - while (fgets(buf, 1024, stream)) { + while (getline(&line, &linecap, stream) != -1) { Function *tmp; - tmp = process_line(buf); + tmp = process_line(line); if (tmp) { debug(2, "New function: `%s'", tmp->name); @@ -1300,5 +1301,6 @@ read_config_file(char *file) { list_of_functions = tmp; } } + free(line); fclose(stream); }
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Thu, 3 Aug 2023 17:08:40 +0200 Subject: pipe[2]() with fds; sysconf(); m[un]map[64](); operator new/delete; regex.h suite $ grep pipe ll pipe2(0x7ffcd6d7baf0, 0x80000, 0x7ffcd6d7bb70, 0x7ffcd6d7bb70) = 0 $ grep pipe ll pipe2([3, 4], 0x80000) = 0 nabijaczleweli@tarta:~/backports/ltrace$ grep -e mmap -e sysconf ll sysconf(30, 0, 1, 0x393c) = 4096 mmap64(0, 0x3d3c, 1, 2) = 0x7fe67b29c000 nabijaczleweli@tarta:~/backports/ltrace$ grep -e mmap -e sysconf ll sysconf(_SC_PAGE_SIZE) = 4096 mmap64(0, 15676, 0b1, 0x2, 0, 4096) = 0x7fa1b27ff000 regcomp(0x7fff5ee83120, ";", 0b100) = REG_OK regexec(0x7fff5ee83040, "\nint SYS_access(string,octal);"..., 1, [{0, 31}], 0b111) = REG_NOMATCH regexec(0x7fff5ee83040, ";\nint SYS_access(string,octal)"..., 1, [{0, 32}], 0b111) = REG_OK regcomp(0x7ffece59ee70, "\\(", 0b100) = REG_EPAREN regerror(REG_EPAREN, 0x7ffece59ee70, nil, 0) = 18 malloc(18) = 0x5604c35c2140 regerror(REG_EPAREN, 0x7ffece59ee70, "Unmatched ( or \\(", 18) = 18 --- etc/ltrace.conf | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/etc/ltrace.conf b/etc/ltrace.conf index 3d4f5cd..ad5a1a4 100644 --- a/etc/ltrace.conf +++ b/etc/ltrace.conf @@ -375,7 +375,9 @@ addr sbrk(long); int getpagesize(); long lseek(int,long,lseek_whence); long lseek64(int,llong,lseek_whence); -int pipe(addr); +int pipe(+array(int,2)); +int pipe2(+array(int,2),hex(int)); +long sysconf(enum(_SC_ARG_MAX=0, _SC_CHILD_MAX=1, _SC_CLK_TCK=2, _SC_NGROUPS_MAX=3, _SC_OPEN_MAX=4, _SC_STREAM_MAX=5, _SC_TZNAME_MAX=6, _SC_JOB_CONTROL=7, _SC_SAVED_IDS=8, _SC_REALTIME_SIGNALS=9, _SC_PRIORITY_SCHEDULING=10, _SC_TIMERS=11, _SC_ASYNCHRONOUS_IO=12, _SC_PRIORITIZED_IO=13, _SC_SYNCHRONIZED_IO=14, _SC_FSYNC=15, _SC_MAPPED_FILES=16, _SC_MEMLOCK=17, _SC_MEMLOCK_RANGE=18, _SC_MEMORY_PROTECTION=19, _SC_MESSAGE_PASSING=20, _SC_SEMAPHORES=21, _SC_SHARED_MEMORY_OBJECTS=22, _SC_AIO_LISTIO_MAX=23, _SC_AIO_MAX=24, _SC_AIO_PRIO_DELTA_MAX=25, _SC_DELAYTIMER_MAX=26, _SC_MQ_OPEN_MAX=27, _SC_MQ_PRIO_MAX=28, _SC_VERSION=29, _SC_PAGE_SIZE=30, _SC_PAGESIZE=30, _SC_RTSIG_MAX=31, _SC_SEM_NSEMS_MAX=32, _SC_SEM_VALUE_MAX=33, _SC_SIGQUEUE_MAX=34, _SC_TIMER_MAX=35, _SC_BC_BASE_MAX=36, _SC_BC_DIM_MAX=37, _SC_BC_SCALE_MAX=38, _SC_BC_STRING_MAX=39, _SC_COLL_WEIGHTS_MAX=40, _SC_EXPR_NEST_MAX=42, _SC_LINE_MAX=43, _SC_RE_DUP_MAX=44, _SC_2_VERSION=46, _SC_2_C_BIND=47, _SC_2_C_DEV=48, _SC_2_FORT_DEV=49, _SC_2_FORT_RUN=50, _SC_2_SW_DEV=51, _SC_2_LOCALEDEF=52, _SC_UIO_MAXIOV=60, _SC_IOV_MAX=60, _SC_THREADS=67, _SC_THREAD_SAFE_FUNCTIONS=68, _SC_GETGR_R_SIZE_MAX=69, _SC_GETPW_R_SIZE_MAX=70, _SC_LOGIN_NAME_MAX=71, _SC_TTY_NAME_MAX=72, _SC_THREAD_DESTRUCTOR_ITERATIONS=73, _SC_THREAD_KEYS_MAX=74, _SC_THREAD_STACK_MIN=75, _SC_THREAD_THREADS_MAX=76, _SC_THREAD_ATTR_STACKADDR=77, _SC_THREAD_ATTR_STACKSIZE=78, _SC_THREAD_PRIORITY_SCHEDULING=79, _SC_THREAD_PRIO_INHERIT=80, _SC_THREAD_PRIO_PROTECT=81, _SC_THREAD_PROCESS_SHARED=82, _SC_NPROCESSORS_CONF=83, _SC_NPROCESSORS_ONLN=84, _SC_PHYS_PAGES=85, _SC_AVPHYS_PAGES=86, _SC_ATEXIT_MAX=87, _SC_PASS_MAX=88, _SC_XOPEN_VERSION=89, _SC_XOPEN_XCU_VERSION=90, _SC_XOPEN_UNIX=91, _SC_XOPEN_CRYPT=92, _SC_XOPEN_ENH_I18N=93, _SC_XOPEN_SHM=94, _SC_2_CHAR_TERM=95, _SC_2_UPE=97, _SC_XOPEN_XPG2=98, _SC_XOPEN_XPG3=99, _SC_XOPEN_XPG4=100, _SC_NZERO=109, _SC_XBS5_ILP32_OFF32=125, _SC_XBS5_ILP32_OFFBIG=126, _SC_XBS5_LP64_OFF64=127, _SC_XBS5_LPBIG_OFFBIG=128, _SC_XOPEN_LEGACY=129, _SC_XOPEN_REALTIME=130, _SC_XOPEN_REALTIME_THREADS=131, _SC_ADVISORY_INFO=132, _SC_BARRIERS=133, _SC_CLOCK_SELECTION=137, _SC_CPUTIME=138, _SC_THREAD_CPUTIME=139, _SC_MONOTONIC_CLOCK=149, _SC_READER_WRITER_LOCKS=153, _SC_SPIN_LOCKS=154, _SC_REGEXP=155, _SC_SHELL=157, _SC_SPAWN=159, _SC_SPORADIC_SERVER=160, _SC_THREAD_SPORADIC_SERVER=161, _SC_TIMEOUTS=164, _SC_TYPED_MEMORY_OBJECTS=165, _SC_2_PBS=168, _SC_2_PBS_ACCOUNTING=169, _SC_2_PBS_LOCATE=170, _SC_2_PBS_MESSAGE=171, _SC_2_PBS_TRACK=172, _SC_SYMLOOP_MAX=173, _SC_STREAMS=174, _SC_2_PBS_CHECKPOINT=175, _SC_V6_ILP32_OFF32=176, _SC_V6_ILP32_OFFBIG=177, _SC_V6_LP64_OFF64=178, _SC_V6_LPBIG_OFFBIG=179, _SC_HOST_NAME_MAX=180, _SC_TRACE=181, _SC_TRACE_EVENT_FILTER=182, _SC_TRACE_INHERIT=183, _SC_TRACE_LOG=184, _SC_IPV6=235, _SC_RAW_SOCKETS=236, _SC_V7_ILP32_OFF32=237, _SC_V7_ILP32_OFFBIG=238, _SC_V7_LP64_OFF64=239, _SC_V7_LPBIG_OFFBIG=240, _SC_SS_REPL_MAX=241, _SC_TRACE_EVENT_NAME_MAX=242, _SC_TRACE_NAME_MAX=243, _SC_TRACE_SYS_MAX=244, _SC_TRACE_USER_EVENT_MAX=245, _SC_XOPEN_STREAMS=246, _SC_THREAD_ROBUST_PRIO_INHERIT=247, _SC_THREAD_ROBUST_PRIO_PROTECT=248, _SC_MINSIGSTKSZ=249, _SC_SIGSTKSZ=250)) ; sys/uio.h typedef iovec = struct(string[elt2],ulong); @@ -449,9 +451,23 @@ addr acl_from_mode(octal); int acl_get_perm(addr,uint); string acl_to_any_text(addr,string,char,int); +; sys/mman.h +addr mmap(addr,ulong,bin(int),hex(int),int,long); +addr mmap64(addr,ulong,bin(int),hex(int),int,llong); +int munmap(addr, ulong); + ; cxxabi.h +addr _Znwm(ulong); operator new +void _ZdlPv(addr); operator delete void __cxa_finalize(addr); +; regex.h +typedef regmatch_t = struct(int,int); broken, needs to be long or llong in glibc! +typedef regerror_t = enum(REG_OK=0,REG_NOMATCH,REG_BADPAT,REG_ECOLLATE,REG_ECTYPE,REG_EESCAPE,REG_ESUBREG,REG_EBRACK,REG_EPAREN,REG_EBRACE,REG_BADBR,REG_ERANGE,REG_ESPACE,REG_BADRPT,REG_EEND,REG_ESIZE,REG_ERPAREN); +regerror_t regcomp(addr,string,bin(int)); +regerror_t regexec(addr,string,ulong,array(regmatch_t,arg3),bin(int)); +ulong regerror(regerror_t,addr,+string0,ulong); + ; other symbols not included above long a64l(string); string l64a(long);
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Thu, 3 Aug 2023 17:51:32 +0200 Subject: Don't malloc file/addr typedefs. Turn opt_p into an array. Just read -Fs as they're encountered. Open -o "we" instead of "w" then setting O_CLOEXEC manually. Put default_prototype in .bss instead of allocating for it thrice Instead of storing them in a list, linking them, then killing the list. And strdup()ing the paths This also means you can syntax/typecheck your config without actually running anything --- common.h | 3 +- libltrace.c | 67 +++++++++++--------------- options.c | 89 ++++++++++------------------------- options.h | 22 ++------- output.c | 56 ++++++---------------- read_config_file.c | 117 +++++++++++++++++++++------------------------- read_config_file.h | 3 +- sysdeps/linux-gnu/trace.c | 10 ++-- 8 files changed, 128 insertions(+), 239 deletions(-) diff --git a/common.h b/common.h index f333f3f..5464d61 100644 --- a/common.h +++ b/common.h @@ -28,6 +28,7 @@ #include <sys/types.h> #include <sys/time.h> #include <stdio.h> +#include <stdbool.h> #include "ltrace.h" #include "defs.h" @@ -55,9 +56,9 @@ struct Function { const char * name; struct param *params; struct arg_type_info *return_info; - int own_return_info; size_t num_params; Function * next; + bool own_return_info; }; extern Function * list_of_functions; diff --git a/libltrace.c b/libltrace.c index 559edfa..873ce98 100644 --- a/libltrace.c +++ b/libltrace.c @@ -44,11 +44,10 @@ stop_non_p_processes(Process *proc, void *data) { int stop = 1; - struct opt_p_t *it; - for (it = opt_p; it != NULL; it = it->next) { - Process * p_proc = pid2proc(it->pid); + for (size_t i = 0; i < opt_p_len; ++i) { + Process * p_proc = pid2proc(opt_p[i]); if (p_proc == NULL) { - printf("stop_non_p_processes: %d terminated?\n", it->pid); + printf("stop_non_p_processes: %d terminated?\n", opt_p[i]); continue; } if (p_proc == proc || p_proc->leader == proc->leader) { @@ -97,42 +96,31 @@ normal_exit(void) } } +/* If filename begins with ~, expand it to the user's home */ +/* directory. This does not correctly handle ~yoda, but that */ +/* isn't as bad as it seems because the shell will normally */ +/* be doing the expansion for us; only the hardcoded */ +/* ~/.ltrace.conf should ever use this code. */ +static void +opt_F_handler(const char *F) +{ + char path[PATH_MAX]; + if (F[0] == '~') { + char *home_dir = getenv("HOME"); + if (home_dir && snprintf(path, sizeof(path), "%s%s", home_dir, F + 1) < (int)sizeof(path)) + read_config_file(path); + } else { + read_config_file(F); + } +} + void ltrace_init(int argc, char **argv) { - struct opt_p_t *opt_p_tmp; - atexit(normal_exit); signal(SIGINT, signal_exit); /* Detach processes when interrupted */ signal(SIGTERM, signal_exit); /* ... or killed */ - argv = process_options(argc, argv); - init_global_config(); - while (opt_F) { - /* If filename begins with ~, expand it to the user's home */ - /* directory. This does not correctly handle ~yoda, but that */ - /* isn't as bad as it seems because the shell will normally */ - /* be doing the expansion for us; only the hardcoded */ - /* ~/.ltrace.conf should ever use this code. */ - if (opt_F->filename[0] == '~') { - char path[PATH_MAX]; - char *home_dir = getenv("HOME"); - if (home_dir) { - strncpy(path, home_dir, PATH_MAX - 1); - path[PATH_MAX - 1] = '\0'; - strncat(path, opt_F->filename + 1, - PATH_MAX - strlen(path) - 1); - read_config_file(path); - } - } else { - read_config_file(opt_F->filename); - } - - struct opt_F_t *next = opt_F->next; - if (opt_F->own_filename) - free(opt_F->filename); - free(opt_F); - opt_F = next; - } + argv = process_options(argc, argv, opt_F_handler); if (command) { /* Check that the binary ABI is supported before * calling execute_program. */ @@ -151,11 +139,8 @@ ltrace_init(int argc, char **argv) { trace_set_options(proc); continue_process(pid); } - opt_p_tmp = opt_p; - while (opt_p_tmp) { - open_pid(opt_p_tmp->pid); - opt_p_tmp = opt_p_tmp->next; - } + for (size_t i = 0; i < opt_p_len; ++i) + open_pid(opt_p[i]); } static int num_ltrace_callbacks[EVENT_MAX]; @@ -163,7 +148,7 @@ static callback_func * ltrace_callbacks[EVENT_MAX]; void ltrace_add_callback(callback_func func, Event_type type) { - ltrace_callbacks[type] = realloc(ltrace_callbacks[type], (num_ltrace_callbacks[type]+1)*sizeof(callback_func)); + ltrace_callbacks[type] = reallocarray(ltrace_callbacks[type], num_ltrace_callbacks[type] + 1, sizeof(callback_func)); ltrace_callbacks[type][num_ltrace_callbacks[type]++] = func; } @@ -171,7 +156,7 @@ static void dispatch_callbacks(Event * ev) { int i; /* Ignoring case 1: signal into a dying tracer */ - if (ev->type==EVENT_SIGNAL && + if (ev->type==EVENT_SIGNAL && exiting && ev->e_un.signum == SIGSTOP) { return; } diff --git a/options.c b/options.c index 1e19dc7..f47aef9 100644 --- a/options.c +++ b/options.c @@ -63,17 +63,15 @@ struct options_t options = { .follow = 0, /* trace child processes */ }; -static char *progname; /* Program name (`ltrace') */ -int opt_i = 0; /* instruction pointer */ +static const char *progname; /* Program name (`ltrace') */ +bool opt_i = false; /* instruction pointer */ int opt_r = 0; /* print relative timestamp */ int opt_t = 0; /* print absolute timestamp */ int opt_T = 0; /* show the time spent inside each call */ /* List of pids given to option -p: */ -struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */ - -/* List of filenames give to option -F: */ -struct opt_F_t *opt_F = NULL; /* alternate configuration file(s) */ +pid_t *opt_p; /* attach to process with a given pid */ +size_t opt_p_len; static void err_usage(void) { @@ -122,7 +120,7 @@ usage(void) { static void usage_debug(void) { fprintf(stdout, "%s debugging option, --debug=<octal> or -D<octal>:\n", progname); - fprintf(stdout, + fprintf(stdout, "\n" " number ref. in source description\n" " 1 general Generally helpful progress information\n" @@ -443,7 +441,7 @@ parse_int(const char *optarg, char opt, int min, int max) } char ** -process_options(int argc, char **argv) +process_options(int argc, char **argv, void(*opt_F)(const char *)) { progname = argv[0]; options.output = stderr; @@ -454,13 +452,14 @@ process_options(int argc, char **argv) guess_cols(); - int libcalls = 1; + bool opt_F_any = false; + bool libcalls = true; while (1) { int c; char *p; int option_index = 0; - static struct option long_options[] = { + static const struct option long_options[] = { {"align", 1, 0, 'a'}, {"config", 1, 0, 'F'}, {"debug", 1, 0, 'D'}, @@ -529,28 +528,14 @@ process_options(int argc, char **argv) options.follow = 1; break; case 'F': - { - struct opt_F_t *tmp = malloc(sizeof(*tmp)); - if (tmp == NULL) { - fail: - fprintf(stderr, "%s\n", - strerror(errno)); - free(tmp); - exit(1); - } - tmp->filename = strdup(optarg); - if (tmp->filename == NULL) - goto fail; - tmp->own_filename = 1; - tmp->next = opt_F; - opt_F = tmp; - break; - } + opt_F(optarg); + opt_F_any = true; + break; case 'h': usage(); exit(0); case 'i': - opt_i++; + opt_i = true; break; case 'l': { @@ -563,34 +548,28 @@ process_options(int argc, char **argv) } case 'L': - libcalls = 0; + libcalls = false; break; case 'n': options.indent = parse_int(optarg, 'n', 0, 20); break; case 'o': - options.output = fopen(optarg, "w"); + options.output = fopen(optarg, "we"); if (!options.output) { fprintf(stderr, "can't open %s for writing: %s\n", optarg, strerror(errno)); exit(1); } - setvbuf(options.output, (char *)NULL, _IOLBF, 0); - fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC); + setvbuf(options.output, NULL, _IOLBF, 0); break; case 'p': - { - struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t)); - if (!tmp) { - perror("ltrace: malloc"); - exit(1); - } - tmp->pid = parse_int(optarg, 'p', 1, 0); - tmp->next = opt_p; - opt_p = tmp; - break; + if (!(opt_p = reallocarray(opt_p, ++opt_p_len, sizeof(*opt_p)))) { + perror("ltrace: malloc"); + exit(1); } + opt_p[opt_p_len - 1] = parse_int(optarg, 'p', 1, 0); + break; case 'r': opt_r++; break; @@ -633,29 +612,9 @@ process_options(int argc, char **argv) argc -= optind; argv += optind; - if (!opt_F) { - opt_F = malloc(sizeof(struct opt_F_t)); - opt_F->next = malloc(sizeof(struct opt_F_t)); - opt_F->next->next = NULL; - opt_F->filename = USER_CONFIG_FILE; - opt_F->own_filename = 0; - opt_F->next->filename = SYSTEM_CONFIG_FILE; - opt_F->next->own_filename = 0; - } - /* Reverse the config file list since it was built by - * prepending, and it would make more sense to process the - * files in the order they were given. Probably it would make - * more sense to keep a tail pointer instead? */ - { - struct opt_F_t *egg = NULL; - struct opt_F_t *chicken; - while (opt_F) { - chicken = opt_F->next; - opt_F->next = egg; - egg = opt_F; - opt_F = chicken; - } - opt_F = egg; + if (!opt_F_any) { + opt_F(SYSTEM_CONFIG_FILE); + opt_F(USER_CONFIG_FILE); } /* If neither -e, nor -l, nor -L are used, set default -e. diff --git a/options.h b/options.h index e1cc823..be862dd 100644 --- a/options.h +++ b/options.h @@ -22,6 +22,7 @@ */ #include <stdio.h> +#include <stdbool.h> #include <sys/types.h> #include "forward.h" @@ -53,25 +54,12 @@ struct options_t { }; extern struct options_t options; -extern int opt_i; /* instruction pointer */ +extern bool opt_i; /* instruction pointer */ extern int opt_r; /* print relative timestamp */ extern int opt_t; /* print absolute timestamp */ extern int opt_T; /* show the time spent inside each call */ -struct opt_p_t { - pid_t pid; - struct opt_p_t *next; -}; - -struct opt_F_t -{ - struct opt_F_t *next; - char *filename; - int own_filename : 1; -}; - -extern struct opt_p_t *opt_p; /* attach to process with a given pid */ - -extern struct opt_F_t *opt_F; /* alternate configuration file(s) */ +extern pid_t *opt_p; /* attach to process with a given pid */ +extern size_t opt_p_len; /* attach to process with a given pid */ -extern char **process_options(int argc, char **argv); +extern char **process_options(int argc, char **argv, void(*opt_F)(const char *)); diff --git a/output.c b/output.c index c1eda23..dc0be31 100644 --- a/output.c +++ b/output.c @@ -139,55 +139,28 @@ begin_of_line(Process *proc, int is_func, int indent) static struct arg_type_info * get_unknown_type(void) { - static struct arg_type_info *info = NULL; - if (info == NULL) { - info = malloc(sizeof(*info)); - if (info == NULL) { - report_global_error("malloc: %s", strerror(errno)); - abort(); - } - *info = *type_get_simple(ARGTYPE_LONG); - info->lens = &guess_lens; - } - return info; + static struct arg_type_info info; + info = *type_get_simple(ARGTYPE_LONG); + info.lens = &guess_lens; + return &info; } /* The default prototype is: long X(long, long, long, long). */ -static Function * -build_default_prototype(void) +static void +build_default_prototype(Function *ret) { - Function *ret = malloc(sizeof(*ret)); - size_t i = 0; - if (ret == NULL) - goto err; - memset(ret, 0, sizeof(*ret)); + static typeof(*ret->params) params[4]; struct arg_type_info *unknown_type = get_unknown_type(); ret->return_info = unknown_type; ret->own_return_info = 0; - ret->num_params = 4; - ret->params = malloc(sizeof(*ret->params) * ret->num_params); - if (ret->params == NULL) - goto err; + ret->num_params = sizeof(params) / sizeof(*params); + ret->params = params; - for (i = 0; i < ret->num_params; ++i) + for (size_t i = 0; i < ret->num_params; ++i) param_init_type(&ret->params[i], unknown_type, 0); - - return ret; - -err: - report_global_error("malloc: %s", strerror(errno)); - if (ret->params != NULL) { - while (i-- > 0) - param_destroy(&ret->params[i]); - free(ret->params); - } - - free(ret); - - return NULL; } static Function * @@ -202,11 +175,10 @@ name2func(char const *name) { return tmp; } - static Function *def = NULL; - if (def == NULL) - def = build_default_prototype(); - - return def; + static Function def; + if (def.params == NULL) + build_default_prototype(&def); + return &def; } void diff --git a/read_config_file.c b/read_config_file.c index 561351d..bcdf2c8 100644 --- a/read_config_file.c +++ b/read_config_file.c @@ -42,20 +42,20 @@ #include "lens_enum.h" static int line_no; -static char *filename; +static const char *filename; struct typedef_node_t; static struct arg_type_info *parse_nonpointer_type(char **str, struct param **extra_param, - size_t param_num, int *ownp, + size_t param_num, bool *ownp, struct typedef_node_t *td); static struct arg_type_info *parse_type(char **str, struct param **extra_param, - size_t param_num, int *ownp, + size_t param_num, bool *ownp, struct typedef_node_t *in_typedef); static struct arg_type_info *parse_lens(char **str, struct param **extra_param, - size_t param_num, int *ownp, + size_t param_num, bool *ownp, struct typedef_node_t *in_typedef); -static int parse_enum(char **str, struct arg_type_info **retp, int *ownp); +static int parse_enum(char **str, struct arg_type_info **retp, bool *ownp); Function *list_of_functions = NULL; @@ -226,15 +226,15 @@ parse_char(char **str, char expected) return 0; } -static struct expr_node *parse_argnum(char **str, int *ownp, int zero); +static struct expr_node *parse_argnum(char **str, bool *ownp, int zero); static struct expr_node * -parse_zero(char **str, struct expr_node *ret, int *ownp) +parse_zero(char **str, struct expr_node *ret, bool *ownp) { eat_spaces(str); if (**str == '(') { ++*str; - int own; + bool own; struct expr_node *arg = parse_argnum(str, &own, 0); if (arg == NULL) return NULL; @@ -248,12 +248,12 @@ parse_zero(char **str, struct expr_node *ret, int *ownp) struct expr_node *ret = build_zero_w_arg(arg, own); if (ret == NULL) goto fail; - *ownp = 1; + *ownp = true; return ret; } else { free(ret); - *ownp = 0; + *ownp = false; return expr_node_zero(); } } @@ -276,7 +276,7 @@ wrap_in_zero(struct expr_node **nodep) * N : The numeric value N */ static struct expr_node * -parse_argnum(char **str, int *ownp, int zero) +parse_argnum(char **str, bool *ownp, int zero) { struct expr_node *expr = malloc(sizeof(*expr)); if (expr == NULL) @@ -294,7 +294,7 @@ parse_argnum(char **str, int *ownp, int zero) if (zero && wrap_in_zero(&expr) < 0) goto fail; - *ownp = 1; + *ownp = true; return expr; } else { @@ -352,7 +352,7 @@ parse_argnum(char **str, int *ownp, int zero) goto fail_ident; free(name); - *ownp = 1; + *ownp = true; return expr; } @@ -364,10 +364,16 @@ fail: struct typedef_node_t { char *name; struct arg_type_info *info; - int own_type; - int forward : 1; struct typedef_node_t *next; -} *typedefs = NULL; + bool own_type; + bool forward : 1; +}; + +static struct arg_type_info void_pointer[] = {{.type = ARGTYPE_POINTER, .u.ptr_info.info = &void_pointer[1]}, {.type = ARGTYPE_VOID}}; +static struct typedef_node_t file_typedef = {.name = (char *)"file", .info = void_pointer}; +static struct typedef_node_t addr_typedef = {.name = (char *)"addr", .info = void_pointer, .next = &file_typedef}; +static struct typedef_node_t *typedefs = &addr_typedef; + static struct typedef_node_t * lookup_typedef(const char *name) @@ -410,13 +416,13 @@ insert_typedef(struct typedef_node_t *td) } static struct typedef_node_t * -new_typedef(char *name, struct arg_type_info *info, int own_type) +new_typedef(char *name, struct arg_type_info *info, bool own_type) { struct typedef_node_t *binding = malloc(sizeof(*binding)); binding->name = name; binding->info = info; binding->own_type = own_type; - binding->forward = 0; + binding->forward = false; binding->next = NULL; return binding; } @@ -449,7 +455,7 @@ parse_typedef(char **str) } eat_spaces(str); - struct typedef_node_t *this_td = new_typedef(name, NULL, 0); + struct typedef_node_t *this_td = new_typedef(name, NULL, false); this_td->info = parse_lens(str, NULL, 0, &this_td->own_type, this_td); if (this_td->info == NULL) { @@ -484,7 +490,7 @@ parse_typedef(char **str) assert(this_td->own_type); type_destroy(forward->info); *forward->info = *this_td->info; - forward->forward = 0; + forward->forward = false; free(this_td->info); free(name); free(this_td); @@ -523,7 +529,7 @@ parse_struct(char **str, struct arg_type_info *info, /* Forward declaration is currently handled as an * empty struct. */ type_init_struct(info); - in_typedef->forward = 1; + in_typedef->forward = true; return 0; } @@ -546,7 +552,7 @@ parse_struct(char **str, struct arg_type_info *info, parse_char(str, ','); eat_spaces(str); - int own; + bool own; struct arg_type_info *field = parse_lens(str, NULL, 0, &own, NULL); if (field == NULL || type_struct_add(info, field, own)) { @@ -557,7 +563,7 @@ parse_struct(char **str, struct arg_type_info *info, } static int -parse_string(char **str, struct arg_type_info **retp, int *ownp) +parse_string(char **str, struct arg_type_info **retp, bool *ownp) { struct arg_type_info *info = malloc(sizeof(*info) * 2); if (info == NULL) { @@ -567,7 +573,7 @@ parse_string(char **str, struct arg_type_info **retp, int *ownp) } struct expr_node *length; - int own_length; + bool own_length; int with_arg = 0; if (isdigit(**str)) { @@ -593,7 +599,7 @@ parse_string(char **str, struct arg_type_info **retp, int *ownp) free(length_arg); goto fail; } - own_length = 1; + own_length = true; } else { eat_spaces(str); @@ -627,7 +633,7 @@ parse_string(char **str, struct arg_type_info **retp, int *ownp) } else { /* It was just a simple string after all. */ length = expr_node_zero(); - own_length = 0; + own_length = false; } } @@ -637,7 +643,7 @@ parse_string(char **str, struct arg_type_info **retp, int *ownp) length, own_length); type_init_pointer(&info[0], &info[1], 0); - *ownp = 1; + *ownp = true; } info->lens = &string_lens; @@ -695,7 +701,7 @@ try_parse_kwd(char **str, const char *kwd) /* Make a copy of INFO and set the *OWN bit if it's not already * owned. */ static int -unshare_type_info(struct arg_type_info **infop, int *ownp) +unshare_type_info(struct arg_type_info **infop, bool *ownp) { if (*ownp) return 0; @@ -708,7 +714,7 @@ unshare_type_info(struct arg_type_info **infop, int *ownp) } *ninfo = **infop; *infop = ninfo; - *ownp = 1; + *ownp = true; return 0; } @@ -717,7 +723,7 @@ unshare_type_info(struct arg_type_info **infop, int *ownp) * latter is only valid if the former is non-NULL, which is only in * top-level context. */ static int -parse_alias(char **str, struct arg_type_info **retp, int *ownp, +parse_alias(char **str, struct arg_type_info **retp, bool *ownp, struct param **extra_param, size_t param_num) { /* For backward compatibility, we need to support things like @@ -759,7 +765,7 @@ parse_array(char **str, struct arg_type_info *info) return -1; eat_spaces(str); - int own; + bool own; struct arg_type_info *elt_info = parse_lens(str, NULL, 0, &own, NULL); if (elt_info == NULL) return -1; @@ -768,7 +774,7 @@ parse_array(char **str, struct arg_type_info *info) parse_char(str, ','); eat_spaces(str); - int own_length; + bool own_length; struct expr_node *length = parse_argnum(str, &own_length, 0); if (length == NULL) { if (own) { @@ -790,7 +796,7 @@ parse_array(char **str, struct arg_type_info *info) * enum<type> (keyname[=value],keyname[=value],... ) */ static int -parse_enum(char **str, struct arg_type_info **retp, int *ownp) +parse_enum(char **str, struct arg_type_info **retp, bool *ownp) { /* Optional type argument. */ eat_spaces(str); @@ -820,7 +826,7 @@ parse_enum(char **str, struct arg_type_info **retp, int *ownp) } else { *retp = type_get_simple(ARGTYPE_INT); - *ownp = 0; + *ownp = false; } /* We'll need to set the lens, so unshare. */ @@ -888,7 +894,7 @@ parse_enum(char **str, struct arg_type_info **retp, int *ownp) static struct arg_type_info * parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num, - int *ownp, struct typedef_node_t *in_typedef) + bool *ownp, struct typedef_node_t *in_typedef) { enum arg_type type; if (parse_arg_type(str, &type) < 0) { @@ -898,7 +904,7 @@ parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num, if (simple == NULL) simple = parse_typedef_name(str); if (simple != NULL) { - *ownp = 0; + *ownp = false; return simple; } @@ -921,7 +927,7 @@ parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num, case ARGTYPE_USHORT: case ARGTYPE_FLOAT: case ARGTYPE_DOUBLE: - *ownp = 0; + *ownp = false; return type_get_simple(type); case ARGTYPE_ARRAY: @@ -941,7 +947,7 @@ parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num, "malloc: %s", strerror(errno)); return NULL; } - *ownp = 1; + *ownp = true; if (type == ARGTYPE_ARRAY) { if (parse_array(str, info) < 0) { @@ -986,7 +992,7 @@ name2lens(char **str, int *own_lensp) } static struct arg_type_info * -parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp, +parse_type(char **str, struct param **extra_param, size_t param_num, bool *ownp, struct typedef_node_t *in_typedef) { struct arg_type_info *info @@ -1009,7 +1015,7 @@ parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp, return NULL; } type_init_pointer(outer, info, *ownp); - *ownp = 1; + *ownp = true; (*str)++; info = outer; } else @@ -1019,7 +1025,7 @@ parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp, } static struct arg_type_info * -parse_lens(char **str, struct param **extra_param, size_t param_num, int *ownp, +parse_lens(char **str, struct param **extra_param, size_t param_num, bool *ownp, struct typedef_node_t *in_typedef) { int own_lens; @@ -1034,7 +1040,7 @@ parse_lens(char **str, struct param **extra_param, size_t param_num, int *ownp, if (lens == &octal_lens && **str != '(') { has_args = 0; info = type_get_simple(ARGTYPE_INT); - *ownp = 0; + *ownp = false; } else if (parse_char(str, '(') < 0) { report_error(filename, line_no, "expected type argument after the lens"); @@ -1099,13 +1105,10 @@ param_is_void(struct param *param) static struct arg_type_info * get_hidden_int(void) { - char *str = strdup("hide(int)"); - char *ptr = str; - assert(str != NULL); - int own; + char *ptr = (char *)"hide(int)"; + bool own; struct arg_type_info *info = parse_lens(&ptr, NULL, 0, &own, NULL); assert(info != NULL); - free(str); return info; } @@ -1183,7 +1186,7 @@ process_line(char *buf) { goto err; } - int own; + bool own; struct arg_type_info *type = parse_lens(&str, &extra_param, fun->num_params - have_stop, &own, NULL); @@ -1260,23 +1263,7 @@ process_line(char *buf) { } void -init_global_config(void) -{ - struct arg_type_info *info = malloc(2 * sizeof(*info)); - if (info == NULL) - error(1, errno, "malloc in init_global_config"); - - memset(info, 0, 2 * sizeof(*info)); - info[0].type = ARGTYPE_POINTER; - info[0].u.ptr_info.info = &info[1]; - info[1].type = ARGTYPE_VOID; - - insert_typedef(new_typedef(strdup("addr"), info, 0)); - insert_typedef(new_typedef(strdup("file"), info, 1)); -} - -void -read_config_file(char *file) { +read_config_file(const char *file) { FILE *stream; char *line = NULL; size_t linecap = 0; diff --git a/read_config_file.h b/read_config_file.h index 7c60253..cf00bfb 100644 --- a/read_config_file.h +++ b/read_config_file.h @@ -19,5 +19,4 @@ * 02110-1301 USA */ -extern void read_config_file(char *); -extern void init_global_config(void); +extern void read_config_file(const char *); diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c index 33ebdf2..09fc1a0 100644 --- a/sysdeps/linux-gnu/trace.c +++ b/sysdeps/linux-gnu/trace.c @@ -414,9 +414,8 @@ detach_process(struct Process *leader) proc_each_breakpoint(leader, NULL, retract_breakpoint_cb, NULL); /* Now untrace the process, if it was attached to by -p. */ - struct opt_p_t *it; - for (it = opt_p; it != NULL; it = it->next) { - struct Process *proc = pid2proc(it->pid); + for (size_t i = 0; i < opt_p_len; ++i) { + struct Process *proc = pid2proc(opt_p[i]); if (proc == NULL) continue; if (proc->leader == leader) { @@ -1152,9 +1151,8 @@ continue_after_syscall(struct Process *proc, int sysnum, int ret_p) void os_ltrace_exiting(void) { - struct opt_p_t *it; - for (it = opt_p; it != NULL; it = it->next) { - struct Process *proc = pid2proc(it->pid); + for (size_t i = 0; i < opt_p_len; ++i) { + struct Process *proc = pid2proc(opt_p[i]); if (proc == NULL || proc->leader == NULL) continue; if (ltrace_exiting_install_handler(proc->leader) < 0)
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz> Date: Thu, 3 Aug 2023 19:13:17 +0200 Subject: Speed up function lookups appx. ten-fold Wherein r_c_f is overall read_config_file() time in ns, DUMP: y is hits, overall time in ns and lookup count, and DUMP: y is misses, formatted likewise. The lookups are now logarithmic with respect to function count instead of linear. tsearch() was also evaluated but was both significantly slower and not a good fit (list populated once). This also fixes the debug message to say Replacing/New instead of always new, and makes the precedence explicit. $ ./ltrace -oll -F etc/ltrace.conf tail -r /tmp/1000000a > /dev/null r_c_f: 1289130 DUMP: y: 12311844 w/2930 DUMP: n: 24297 w/2 $ ./ltrace -oll -F etc/ltrace.conf tail -r /tmp/1000000a > /dev/null r_c_f: 1961890 DUMP: y: 1038683 w/2930 DUMP: n: 1281 w/2 $ { head -c5k; ./ltrace -oll -F etc/ltrace.conf ~/code/voreutils/out/cmd/tac -rs \;; } < etc/ltrace.conf > /dev/null r_c_f: 1308504 DUMP: y: 84145117 w/32066 DUMP: n: 23977 w/2 $ { head -c5k; ./ltrace -oll -F etc/ltrace.conf ~/code/voreutils/out/cmd/tac -rs \;; } < etc/ltrace.conf > /dev/null r_c_f: 2605150 DUMP: y: 9162137 w/32066 DUMP: n: 1069 w/2 Instrumented thusly on amd64: #include <time.h> void read_config_file(char *file) { struct timespec s, e; clock_gettime(CLOCK_MONOTONIC, &s); FILE *stream; char *line = NULL; size_t linecap = 0; filename = file; stream = fopen(filename, "r"); if (!stream) { return; } debug(1, "Reading config file `%s'...", filename); line_no = 0; while (getline(&line, &linecap, stream) != -1) { Function *tmp; tmp = process_line(line); if (tmp) { debug(2, "New function: `%s'", tmp->name); tmp->next = list_of_functions; list_of_functions = tmp; } } free(line); fclose(stream); clock_gettime(CLOCK_MONOTONIC, &e); assert(s.tv_sec == e.tv_sec); fprintf(stderr, "r_c_f: %lu\n", e.tv_nsec - s.tv_nsec); } #include <time.h> static uint64_t totalnsec_y; static unsigned totalcnt_y; static uint64_t totalnsec_n; static unsigned totalcnt_n; static Function * name2func(char const *name) { Function *tmp; struct timespec s, e; clock_gettime(CLOCK_MONOTONIC, &s); for (tmp = list_of_functions; tmp != NULL; tmp = tmp->next) { if (!strcmp(tmp->name, name)) { clock_gettime(CLOCK_MONOTONIC, &e); assert(s.tv_sec == e.tv_sec); ++totalcnt_y; totalnsec_y += e.tv_nsec - s.tv_nsec; return tmp; } } static Function def; if (def.params == NULL) build_default_prototype(&def); clock_gettime(CLOCK_MONOTONIC, &e); assert(s.tv_sec == e.tv_sec); ++totalcnt_n; totalnsec_n += e.tv_nsec - s.tv_nsec; return &def; } __attribute__((destructor)) void dumpy(void) { fprintf(stderr, "DUMP: y: %lu w/%u\n", totalnsec_y, totalcnt_y); fprintf(stderr, "DUMP: n: %lu w/%u\n", totalnsec_n, totalcnt_n); } --- common.h | 9 ++++++++- output.c | 39 ++++++++++++++++----------------------- read_config_file.c | 44 +++++++++++++++++++++++++------------------- 3 files changed, 49 insertions(+), 43 deletions(-) diff --git a/common.h b/common.h index 5464d61..56ba479 100644 --- a/common.h +++ b/common.h @@ -27,6 +27,7 @@ #include <sys/types.h> #include <sys/time.h> +#include <string.h> #include <stdio.h> #include <stdbool.h> @@ -57,13 +58,19 @@ struct Function { struct param *params; struct arg_type_info *return_info; size_t num_params; - Function * next; bool own_return_info; }; extern Function * list_of_functions; +extern size_t list_of_functions_size; extern char *PLTs_initialized_by_here; +static inline int Function_cmp(const void *l, const void *r) { + const Function *lf = l; + const Function *rf = r; + return strcmp(lf->name, rf->name); +} + struct opt_c_struct { int count; struct timeval tv; diff --git a/output.c b/output.c index dc0be31..273461c 100644 --- a/output.c +++ b/output.c @@ -146,39 +146,32 @@ get_unknown_type(void) } /* The default prototype is: long X(long, long, long, long). */ -static void -build_default_prototype(Function *ret) +static Function * +build_default_prototype(void) { - static typeof(*ret->params) params[4]; + static Function ret; + static typeof(*ret.params) params[4]; + if (ret.params) + return &ret; struct arg_type_info *unknown_type = get_unknown_type(); - ret->return_info = unknown_type; - ret->own_return_info = 0; + ret.return_info = unknown_type; + ret.own_return_info = 0; + + ret.num_params = sizeof(params) / sizeof(*params); + ret.params = params; - ret->num_params = sizeof(params) / sizeof(*params); - ret->params = params; + for (size_t i = 0; i < ret.num_params; ++i) + param_init_type(&ret.params[i], unknown_type, 0); - for (size_t i = 0; i < ret->num_params; ++i) - param_init_type(&ret->params[i], unknown_type, 0); + return &ret; } static Function * name2func(char const *name) { - Function *tmp; - const char *str1, *str2; - - for (tmp = list_of_functions; tmp != NULL; tmp = tmp->next) { - str1 = tmp->name; - str2 = name; - if (!strcmp(str1, str2)) - return tmp; - } - - static Function def; - if (def.params == NULL) - build_default_prototype(&def); - return &def; + const Function key = {.name = name}; + return bsearch(&key, list_of_functions, list_of_functions_size, sizeof(*list_of_functions), Function_cmp) ?: build_default_prototype(); } void diff --git a/read_config_file.c b/read_config_file.c index bcdf2c8..8fe4646 100644 --- a/read_config_file.c +++ b/read_config_file.c @@ -28,6 +28,7 @@ #include <ctype.h> #include <errno.h> #include <error.h> +#include <search.h> #include <assert.h> #include "common.h" @@ -58,6 +59,7 @@ static struct arg_type_info *parse_lens(char **str, struct param **extra_param, static int parse_enum(char **str, struct arg_type_info **retp, bool *ownp); Function *list_of_functions = NULL; +size_t list_of_functions_size = 0; static int parse_arg_type(char **name, enum arg_type *ret) @@ -500,8 +502,6 @@ static void destroy_fun(Function *fun) { size_t i; - if (fun == NULL) - return; if (fun->own_return_info) { type_destroy(fun->return_info); free(fun->return_info); @@ -1112,12 +1112,10 @@ get_hidden_int(void) return info; } -static Function * -process_line(char *buf) { - char *str = buf; +static bool +process_line(char *str, struct Function *fun) { char *tmp; - line_no++; debug(3, "Reading line %d of `%s'", line_no, filename); eat_spaces(&str); @@ -1130,12 +1128,7 @@ process_line(char *buf) { return NULL; } - Function *fun = calloc(1, sizeof(*fun)); - if (fun == NULL) { - report_error(filename, line_no, - "alloc function: %s", strerror(errno)); - return NULL; - } + memset(fun, 0, sizeof(*fun)); fun->return_info = parse_lens(&str, NULL, 0, &fun->own_return_info, NULL); @@ -1267,6 +1260,7 @@ read_config_file(const char *file) { FILE *stream; char *line = NULL; size_t linecap = 0; + bool freeback = false; filename = file; stream = fopen(filename, "r"); @@ -1278,16 +1272,28 @@ read_config_file(const char *file) { line_no = 0; while (getline(&line, &linecap, stream) != -1) { - Function *tmp; - - tmp = process_line(line); + ++line_no; + if(!freeback) { + if(!(list_of_functions = reallocarray(list_of_functions, list_of_functions_size + 1, sizeof(*list_of_functions)))) { + report_error(filename, line_no, + "alloc function: %s", strerror(errno)); + continue; + } + freeback = true; + } - if (tmp) { - debug(2, "New function: `%s'", tmp->name); - tmp->next = list_of_functions; - list_of_functions = tmp; + if (process_line(line, &list_of_functions[list_of_functions_size])) { + Function *old = lfind(&list_of_functions[list_of_functions_size], list_of_functions, &list_of_functions_size, sizeof(*list_of_functions), Function_cmp); + debug(2, "%s function: `%s'", old ? "Replacing" : "New", list_of_functions[list_of_functions_size].name); + if (old) + memcpy(old, &list_of_functions[list_of_functions_size], sizeof(*old)); + else { + ++list_of_functions_size; + freeback = false; + } } } free(line); fclose(stream); + qsort(list_of_functions, list_of_functions_size, sizeof(*list_of_functions), Function_cmp); }
signature.asc
Description: PGP signature