From: Denys Vlasenko Date: Thu, 21 Feb 2013 15:13:47 +0000 (+0100) Subject: Eliminate many SCNO_IS_VALID checks X-Git-Tag: v4.8~131 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=74ec14f968a418691b851cbbfeb0269174c64b08;p=strace Eliminate many SCNO_IS_VALID checks By adding tcp->s_ent pointer tot syscall table entry, we can replace sysent[tcp->scno] references by tcp->s_ent. More importantly, we may ensure that tcp->s_ent is always valid, regardless of tcp->scno value. This allows us to drop SCNO_IS_VALID(tcp->scno) checks before we access syscall table entry. We can optimize (qual_flags[tcp->scno] & QUAL_foo) checks with a similar technique. Resulting code shrink: text data bss dec hex filename 245975 700 19072 265747 40e13 strace.t3/strace 245703 700 19072 265475 40d03 strace.t4/strace * count.c (count_syscall): Use cheaper SCNO_IN_RANGE() check. * defs.h: Add "int qual_flg" and "const struct sysent *s_ent" to struct tcb. Remove "int u_nargs" from it. Add UNDEFINED_SCNO constant which will mark undefined scnos in tcp->qual_flg. * pathtrace.c (pathtrace_match): Drop SCNO_IS_VALID check. Use tcp->s_ent instead of sysent[tcp->scno]. * process.c (sys_prctl): Use tcp->s_ent->nargs instead of tcp->u_nargs. (sys_waitid): Likewise. * strace.c (init): Add compile-time check that DEFAULT_QUAL_FLAGS constant is consistent with init code. * syscall.c (decode_socket_subcall): Use tcp->s_ent->nargs instead of tcp->u_nargs. Set tcp->qual_flg and tcp->s_ent. (decode_ipc_subcall): Likewise. (printargs): Use tcp->s_ent->nargs instead of tcp->u_nargs. (printargs_lu): Likewise. (printargs_ld): Likewise. (get_scno): [MIPS,ALPHA] Use cheaper SCNO_IN_RANGE() check. If !SCNO_IS_VALID, set tcp->s_ent and tcp->qual_flg to default values. (internal_fork): Use tcp->s_ent instead of sysent[tcp->scno]. (syscall_fixup_for_fork_exec): Remove SCNO_IS_VALID check. Use tcp->s_ent instead of sysent[tcp->scno]. (get_syscall_args): Likewise. (get_error): Drop SCNO_IS_VALID check where it is redundant. (dumpio): Drop SCNO_IS_VALID check where it is redundant. Use tcp->s_ent instead of sysent[tcp->scno]. (trace_syscall_entering): Use (tcp->qual_flg & UNDEFINED_SCNO) instead of SCNO_IS_VALID check. Use tcp->s_ent instead of sysent[tcp->scno]. Drop SCNO_IS_VALID check where it is redundant. Print undefined syscall name with undefined_scno_name(tcp). (trace_syscall_exiting): Likewise. * util.c (setbpt): Use tcp->s_ent instead of sysent[tcp->scno]. Signed-off-by: Denys Vlasenko --- diff --git a/count.c b/count.c index 9405fd1b..6c9b1c20 100644 --- a/count.c +++ b/count.c @@ -48,7 +48,9 @@ static struct timeval shortest = { 1000000, 0 }; void count_syscall(struct tcb *tcp, struct timeval *tv) { - if (!SCNO_IS_VALID(tcp->scno)) + unsigned long scno = tcp->scno; + + if (!SCNO_IN_RANGE(scno)) return; if (!counts) { @@ -57,9 +59,9 @@ count_syscall(struct tcb *tcp, struct timeval *tv) die_out_of_memory(); } - counts[tcp->scno].calls++; + counts[scno].calls++; if (tcp->u_error) - counts[tcp->scno].errors++; + counts[scno].errors++; tv_sub(tv, tv, &tcp->etime); if (tv_cmp(tv, &tcp->dtime) > 0) { @@ -87,7 +89,7 @@ count_syscall(struct tcb *tcp, struct timeval *tv) } if (tv_cmp(tv, &shortest) < 0) shortest = *tv; - tv_add(&counts[tcp->scno].time, &counts[tcp->scno].time, tv); + tv_add(&counts[scno].time, &counts[scno].time, tv); } static int diff --git a/defs.h b/defs.h index fc8f8e78..855a3122 100644 --- a/defs.h +++ b/defs.h @@ -366,11 +366,24 @@ extern struct pt_regs arm_regs; extern struct pt_regs tile_regs; #endif +struct sysent { + unsigned nargs; + int sys_flags; + int (*sys_func)(); + const char *sys_name; +}; + +struct ioctlent { + const char *doth; + const char *symbol; + unsigned long code; +}; + /* Trace Control Block */ struct tcb { int flags; /* See below for TCB_ values */ int pid; /* Process Id of this entry */ - int u_nargs; /* System call argument count */ + int qual_flg; /* qual_flags[scno] or DEFAULT_QUAL_FLAGS + RAW */ int u_error; /* Error code */ long scno; /* System call number */ long u_arg[MAX_ARGS]; /* System call arguments */ @@ -385,6 +398,7 @@ struct tcb { int curcol; /* Output column for this process */ FILE *outf; /* Output file for this process */ const char *auxstr; /* Auxiliary info from syscall (see RVAL_STR) */ + const struct sysent *s_ent; /* sysent[scno] or dummy struct for bad scno */ struct timeval stime; /* System time usage as of last process wait */ struct timeval dtime; /* Delta for system time usage */ struct timeval etime; /* Syscall entry time */ @@ -447,14 +461,17 @@ struct tcb { #endif /* qualifier flags */ -#define QUAL_TRACE 0001 /* this system call should be traced */ -#define QUAL_ABBREV 0002 /* abbreviate the structures of this syscall */ -#define QUAL_VERBOSE 0004 /* decode the structures of this syscall */ -#define QUAL_RAW 0010 /* print all args in hex for this syscall */ -#define QUAL_SIGNAL 0020 /* report events with this signal */ -#define QUAL_FAULT 0040 /* report events with this fault */ -#define QUAL_READ 0100 /* dump data read on this file descriptor */ -#define QUAL_WRITE 0200 /* dump data written to this file descriptor */ +#define QUAL_TRACE 0x001 /* this system call should be traced */ +#define QUAL_ABBREV 0x002 /* abbreviate the structures of this syscall */ +#define QUAL_VERBOSE 0x004 /* decode the structures of this syscall */ +#define QUAL_RAW 0x008 /* print all args in hex for this syscall */ +#define QUAL_SIGNAL 0x010 /* report events with this signal */ +#define QUAL_FAULT 0x020 /* report events with this fault */ +#define QUAL_READ 0x040 /* dump data read on this file descriptor */ +#define QUAL_WRITE 0x080 /* dump data written to this file descriptor */ +#define UNDEFINED_SCNO 0x100 /* Used only in tcp->qual_flg */ + +#define DEFAULT_QUAL_FLAGS (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE) #define entering(tcp) (!((tcp)->flags & TCB_INSYSCALL)) #define exiting(tcp) ((tcp)->flags & TCB_INSYSCALL) @@ -728,19 +745,6 @@ extern unsigned current_wordsize; # define widen_to_long(v) ((long)(v)) #endif -struct sysent { - unsigned nargs; - int sys_flags; - int (*sys_func)(); - const char *sys_name; -}; - -struct ioctlent { - const char *doth; - const char *symbol; - unsigned long code; -}; - extern const struct sysent *sysent; extern unsigned nsyscalls; extern const char *const *errnoent; diff --git a/pathtrace.c b/pathtrace.c index 33c9b75d..7f90dd5c 100644 --- a/pathtrace.c +++ b/pathtrace.c @@ -162,10 +162,7 @@ pathtrace_match(struct tcb *tcp) if (selected[0] == NULL) return 1; - if (!SCNO_IS_VALID(tcp->scno)) - return 0; - - s = &sysent[tcp->scno]; + s = tcp->s_ent; if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC))) return 0; diff --git a/process.c b/process.c index 0b702e77..a1f963e6 100644 --- a/process.c +++ b/process.c @@ -302,7 +302,7 @@ sys_prctl(struct tcb *tcp) break; #endif default: - for (i = 1; i < tcp->u_nargs; i++) + for (i = 1; i < tcp->s_ent->nargs; i++) tprintf(", %#lx", tcp->u_arg[i]); break; } @@ -1137,7 +1137,7 @@ sys_waitid(struct tcb *tcp) /* options */ tprints(", "); printflags(wait4_options, tcp->u_arg[3], "W???"); - if (tcp->u_nargs > 4) { + if (tcp->s_ent->nargs > 4) { /* usage */ tprints(", "); if (!tcp->u_arg[4]) diff --git a/strace.c b/strace.c index 1052a885..8d7ee74d 100644 --- a/strace.c +++ b/strace.c @@ -1543,6 +1543,9 @@ init(int argc, char *argv[]) qualify("trace=all"); qualify("abbrev=all"); qualify("verbose=all"); +#if DEFAULT_QUAL_FLAGS != (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE) +# error Bug in DEFAULT_QUAL_FLAGS +#endif qualify("signal=all"); while ((c = getopt(argc, argv, "+bcCdfFhiqrtTvVxyz" diff --git a/syscall.c b/syscall.c index b1a407b0..fa06156b 100644 --- a/syscall.c +++ b/syscall.c @@ -556,16 +556,18 @@ static void decode_socket_subcall(struct tcb *tcp) { unsigned long addr; - unsigned int i, size; + unsigned int i, n, size; if (tcp->u_arg[0] < 0 || tcp->u_arg[0] >= SYS_socket_nsubcalls) return; tcp->scno = SYS_socket_subcall + tcp->u_arg[0]; + tcp->qual_flg = qual_flags[tcp->scno]; + tcp->s_ent = &sysent[tcp->scno]; addr = tcp->u_arg[1]; - tcp->u_nargs = sysent[tcp->scno].nargs; size = current_wordsize; - for (i = 0; i < tcp->u_nargs; ++i) { + n = tcp->s_ent->nargs; + for (i = 0; i < n; ++i) { if (size == sizeof(int)) { unsigned int arg; if (umove(tcp, addr, &arg) < 0) @@ -587,14 +589,16 @@ decode_socket_subcall(struct tcb *tcp) static void decode_ipc_subcall(struct tcb *tcp) { - unsigned int i; + unsigned int i, n; if (tcp->u_arg[0] < 0 || tcp->u_arg[0] >= SYS_ipc_nsubcalls) return; tcp->scno = SYS_ipc_subcall + tcp->u_arg[0]; - tcp->u_nargs = sysent[tcp->scno].nargs; - for (i = 0; i < tcp->u_nargs; i++) + tcp->qual_flg = qual_flags[tcp->scno]; + tcp->s_ent = &sysent[tcp->scno]; + n = tcp->s_ent->nargs; + for (i = 0; i < n; i++) tcp->u_arg[i] = tcp->u_arg[i + 1]; } #endif @@ -604,8 +608,8 @@ printargs(struct tcb *tcp) { if (entering(tcp)) { int i; - - for (i = 0; i < tcp->u_nargs; i++) + int n = tcp->s_ent->nargs; + for (i = 0; i < n; i++) tprintf("%s%#lx", i ? ", " : "", tcp->u_arg[i]); } return 0; @@ -616,8 +620,8 @@ printargs_lu(struct tcb *tcp) { if (entering(tcp)) { int i; - - for (i = 0; i < tcp->u_nargs; i++) + int n = tcp->s_ent->nargs; + for (i = 0; i < n; i++) tprintf("%s%lu", i ? ", " : "", tcp->u_arg[i]); } return 0; @@ -628,8 +632,8 @@ printargs_ld(struct tcb *tcp) { if (entering(tcp)) { int i; - - for (i = 0; i < tcp->u_nargs; i++) + int n = tcp->s_ent->nargs; + for (i = 0; i < n; i++) tprintf("%s%ld", i ? ", " : "", tcp->u_arg[i]); } return 0; @@ -1305,7 +1309,7 @@ get_scno(struct tcb *tcp) mips_r2 = regs[REG_V0]; scno = mips_r2; - if (!SCNO_IS_VALID(scno)) { + if (!SCNO_IN_RANGE(scno)) { if (mips_a3 == 0 || mips_a3 == -1) { if (debug_flag) fprintf(stderr, "stray syscall exit: v0 = %ld\n", scno); @@ -1318,7 +1322,7 @@ get_scno(struct tcb *tcp) if (upeek(tcp, REG_V0, &scno) < 0) return -1; - if (!SCNO_IS_VALID(scno)) { + if (!SCNO_IN_RANGE(scno)) { if (mips_a3 == 0 || mips_a3 == -1) { if (debug_flag) fprintf(stderr, "stray syscall exit: v0 = %ld\n", scno); @@ -1335,7 +1339,7 @@ get_scno(struct tcb *tcp) * Do some sanity checks to figure out if it's * really a syscall entry */ - if (!SCNO_IS_VALID(scno)) { + if (!SCNO_IN_RANGE(scno)) { if (alpha_a3 == 0 || alpha_a3 == -1) { if (debug_flag) fprintf(stderr, "stray syscall exit: r0 = %ld\n", scno); @@ -1453,6 +1457,19 @@ get_scno(struct tcb *tcp) #endif tcp->scno = scno; + if (SCNO_IS_VALID(tcp->scno)) { + tcp->s_ent = &sysent[scno]; + tcp->qual_flg = qual_flags[scno]; + } else { + static const struct sysent unknown = { + .nargs = MAX_ARGS, + .sys_flags = 0, + .sys_func = printargs, + .sys_name = "unknown", /* not used */ + }; + tcp->s_ent = &unknown; + tcp->qual_flg = UNDEFINED_SCNO | QUAL_RAW | DEFAULT_QUAL_FLAGS; + } return 1; } @@ -1567,8 +1584,9 @@ internal_fork(struct tcb *tcp) * CLONE_UNTRACED, so we keep the same logic with that option * and don't trace it. */ - if ((sysent[tcp->scno].sys_func == sys_clone) && - (tcp->u_arg[ARG_FLAGS] & CLONE_UNTRACED)) + if ((tcp->s_ent->sys_func == sys_clone) + && (tcp->u_arg[ARG_FLAGS] & CLONE_UNTRACED) + ) return; setbpt(tcp); } else { @@ -1603,10 +1621,7 @@ syscall_fixup_for_fork_exec(struct tcb *tcp) */ int (*func)(); - if (!SCNO_IS_VALID(tcp->scno)) - return; - - func = sysent[tcp->scno].sys_func; + func = tcp->s_ent->sys_func; if ( sys_fork == func || sys_vfork == func @@ -1634,10 +1649,7 @@ get_syscall_args(struct tcb *tcp) { int i, nargs; - if (SCNO_IS_VALID(tcp->scno)) - nargs = tcp->u_nargs = sysent[tcp->scno].nargs; - else - nargs = tcp->u_nargs = MAX_ARGS; + nargs = tcp->s_ent->nargs; #if defined(S390) || defined(S390X) for (i = 0; i < nargs; ++i) @@ -1874,10 +1886,10 @@ trace_syscall_entering(struct tcb *tcp) printleader(tcp); if (scno_good != 1) tprints("????" /* anti-trigraph gap */ "("); - else if (!SCNO_IS_VALID(tcp->scno)) + else if (tcp->qual_flg & UNDEFINED_SCNO) tprintf("%s(", undefined_scno_name(tcp)); else - tprintf("%s(", sysent[tcp->scno].sys_name); + tprintf("%s(", tcp->s_ent->sys_name); /* * " " will be added later by the code which * detects ptrace errors. @@ -1886,29 +1898,29 @@ trace_syscall_entering(struct tcb *tcp) } #if defined(SYS_socket_subcall) || defined(SYS_ipc_subcall) - while (SCNO_IS_VALID(tcp->scno)) { + while (1) { # ifdef SYS_socket_subcall - if (sysent[tcp->scno].sys_func == sys_socketcall) { + if (tcp->s_ent->sys_func == sys_socketcall) { decode_socket_subcall(tcp); break; } # endif # ifdef SYS_ipc_subcall - if (sysent[tcp->scno].sys_func == sys_ipc) { + if (tcp->s_ent->sys_func == sys_ipc) { decode_ipc_subcall(tcp); break; } # endif break; } -#endif /* SYS_socket_subcall || SYS_ipc_subcall */ +#endif if (need_fork_exec_workarounds) syscall_fixup_for_fork_exec(tcp); - if ((SCNO_IS_VALID(tcp->scno) && - !(qual_flags[tcp->scno] & QUAL_TRACE)) || - (tracing_paths && !pathtrace_match(tcp))) { + if (!(tcp->qual_flg & QUAL_TRACE) + || (tracing_paths && !pathtrace_match(tcp)) + ) { tcp->flags |= TCB_INSYSCALL | TCB_FILTERED; return 0; } @@ -1921,16 +1933,14 @@ trace_syscall_entering(struct tcb *tcp) } printleader(tcp); - if (!SCNO_IS_VALID(tcp->scno)) + if (tcp->qual_flg & UNDEFINED_SCNO) tprintf("%s(", undefined_scno_name(tcp)); else - tprintf("%s(", sysent[tcp->scno].sys_name); - if (!SCNO_IS_VALID(tcp->scno) || - ((qual_flags[tcp->scno] & QUAL_RAW) && - sysent[tcp->scno].sys_func != sys_exit)) + tprintf("%s(", tcp->s_ent->sys_name); + if ((tcp->qual_flg & QUAL_RAW) && tcp->s_ent->sys_func != sys_exit) res = printargs(tcp); else - res = (*sysent[tcp->scno].sys_func)(tcp); + res = tcp->s_ent->sys_func(tcp); fflush(tcp->outf); ret: @@ -2102,9 +2112,7 @@ get_error(struct tcb *tcp) { int u_error = 0; int check_errno = 1; - if (SCNO_IN_RANGE(tcp->scno) - && (sysent[tcp->scno].sys_flags & SYSCALL_NEVER_FAILS) - ) { + if (tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS) { check_errno = 0; } #if defined(S390) || defined(S390X) @@ -2333,31 +2341,32 @@ get_error(struct tcb *tcp) static void dumpio(struct tcb *tcp) { + int (*func)(); + if (syserror(tcp)) return; - if (tcp->u_arg[0] < 0 || tcp->u_arg[0] >= MAX_QUALS) - return; - if (!SCNO_IS_VALID(tcp->scno)) + if ((unsigned long) tcp->u_arg[0] >= MAX_QUALS) return; - if (sysent[tcp->scno].sys_func == printargs) + func = tcp->s_ent->sys_func; + if (func == printargs) return; if (qual_flags[tcp->u_arg[0]] & QUAL_READ) { - if (sysent[tcp->scno].sys_func == sys_read || - sysent[tcp->scno].sys_func == sys_pread || - sysent[tcp->scno].sys_func == sys_recv || - sysent[tcp->scno].sys_func == sys_recvfrom) + if (func == sys_read || + func == sys_pread || + func == sys_recv || + func == sys_recvfrom) dumpstr(tcp, tcp->u_arg[1], tcp->u_rval); - else if (sysent[tcp->scno].sys_func == sys_readv) + else if (func == sys_readv) dumpiov(tcp, tcp->u_arg[2], tcp->u_arg[1]); return; } if (qual_flags[tcp->u_arg[0]] & QUAL_WRITE) { - if (sysent[tcp->scno].sys_func == sys_write || - sysent[tcp->scno].sys_func == sys_pwrite || - sysent[tcp->scno].sys_func == sys_send || - sysent[tcp->scno].sys_func == sys_sendto) + if (func == sys_write || + func == sys_pwrite || + func == sys_send || + func == sys_sendto) dumpstr(tcp, tcp->u_arg[1], tcp->u_arg[2]); - else if (sysent[tcp->scno].sys_func == sys_writev) + else if (func == sys_writev) dumpiov(tcp, tcp->u_arg[2], tcp->u_arg[1]); return; } @@ -2408,10 +2417,10 @@ trace_syscall_exiting(struct tcb *tcp) if ((followfork < 2 && printing_tcp != tcp) || (tcp->flags & TCB_REPRINT)) { tcp->flags &= ~TCB_REPRINT; printleader(tcp); - if (!SCNO_IS_VALID(tcp->scno)) + if (tcp->qual_flg & UNDEFINED_SCNO) tprintf("<... %s resumed> ", undefined_scno_name(tcp)); else - tprintf("<... %s resumed> ", sysent[tcp->scno].sys_name); + tprintf("<... %s resumed> ", tcp->s_ent->sys_name); } printing_tcp = tcp; @@ -2426,8 +2435,7 @@ trace_syscall_exiting(struct tcb *tcp) } sys_res = 0; - if (!SCNO_IS_VALID(tcp->scno) - || (qual_flags[tcp->scno] & QUAL_RAW)) { + if (tcp->qual_flg & QUAL_RAW) { /* sys_res = printargs(tcp); - but it's nop on sysexit */ } else { /* FIXME: not_failing_only (IOW, option -z) is broken: @@ -2440,14 +2448,13 @@ trace_syscall_exiting(struct tcb *tcp) */ if (not_failing_only && tcp->u_error) goto ret; /* ignore failed syscalls */ - sys_res = (*sysent[tcp->scno].sys_func)(tcp); + sys_res = tcp->s_ent->sys_func(tcp); } tprints(") "); tabto(); u_error = tcp->u_error; - if (!SCNO_IS_VALID(tcp->scno) || - qual_flags[tcp->scno] & QUAL_RAW) { + if (tcp->qual_flg & QUAL_RAW) { if (u_error) tprintf("= -1 (errno %ld)", u_error); else diff --git a/util.c b/util.c index f0d024d4..4c22c5fd 100644 --- a/util.c +++ b/util.c @@ -1332,8 +1332,8 @@ setbpt(struct tcb *tcp) } } - if (sysent[tcp->scno].sys_func == sys_fork || - sysent[tcp->scno].sys_func == sys_vfork) { + if (tcp->s_ent->sys_func == sys_fork || + tcp->s_ent->sys_func == sys_vfork) { if (arg_setup(tcp, &state) < 0 || get_arg0(tcp, &state, &tcp->inst[0]) < 0 || get_arg1(tcp, &state, &tcp->inst[1]) < 0 @@ -1349,7 +1349,7 @@ setbpt(struct tcb *tcp) return 0; } - if (sysent[tcp->scno].sys_func == sys_clone) { + if (tcp->s_ent->sys_func == sys_clone) { /* ia64 calls directly `clone (CLONE_VFORK | CLONE_VM)' contrary to x86 vfork above. Even on x86 we turn the vfork semantics into plain fork - each application must not