diff options
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ax-gdb.c | 19 | ||||
-rw-r--r-- | gdb/eval.c | 16 | ||||
-rw-r--r-- | gdb/expop.h | 31 | ||||
-rw-r--r-- | gdb/stap-probe.c | 15 | ||||
-rw-r--r-- | gdb/std-operator.def | 9 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/stap-probe.c | 22 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/stap-probe.exp | 125 |
7 files changed, 237 insertions, 0 deletions
diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c index 0b12dc3..be2063f 100644 --- a/gdb/ax-gdb.c +++ b/gdb/ax-gdb.c @@ -1826,6 +1826,25 @@ unop_cast_operation::do_generate_ax (struct expression *exp, } void +unop_extract_operation::do_generate_ax (struct expression *exp, + struct agent_expr *ax, + struct axs_value *value, + struct type *cast_type) +{ + std::get<0> (m_storage)->generate_ax (exp, ax, value); + + struct type *to_type = get_type (); + + if (!is_scalar_type (to_type)) + error (_("can't generate agent expression to extract non-scalar type")); + + if (to_type->is_unsigned ()) + gen_extend (ax, to_type); + else + gen_sign_extend (ax, to_type); +} + +void unop_memval_operation::do_generate_ax (struct expression *exp, struct agent_expr *ax, struct axs_value *value, @@ -2460,6 +2460,22 @@ array_operation::evaluate (struct type *expect_type, return value_array (tem2, tem3, argvec); } +value * +unop_extract_operation::evaluate (struct type *expect_type, + struct expression *exp, + enum noside noside) +{ + value *old_value = std::get<0> (m_storage)->evaluate (nullptr, exp, noside); + struct type *type = get_type (); + + if (TYPE_LENGTH (type) > TYPE_LENGTH (value_type (old_value))) + error (_("length type is larger than the value type")); + + struct value *result = allocate_value (type); + value_contents_copy (result, 0, old_value, 0, TYPE_LENGTH (type)); + return result; +} + } diff --git a/gdb/expop.h b/gdb/expop.h index d903ab0..a346570 100644 --- a/gdb/expop.h +++ b/gdb/expop.h @@ -1947,6 +1947,37 @@ protected: override; }; +/* Not a cast! Extract a value of a given type from the contents of a + value. The new value is extracted from the least significant bytes + of the old value. The new value's type must be no bigger than the + old values type. */ +class unop_extract_operation + : public maybe_constant_operation<operation_up, struct type *> +{ +public: + + using maybe_constant_operation::maybe_constant_operation; + + value *evaluate (struct type *expect_type, struct expression *exp, + enum noside noside) override; + + enum exp_opcode opcode () const override + { return UNOP_EXTRACT; } + + /* Return the type referenced by this object. */ + struct type *get_type () const + { + return std::get<1> (m_storage); + } + +protected: + + void do_generate_ax (struct expression *exp, + struct agent_expr *ax, + struct axs_value *value, + struct type *cast_type) override; +}; + /* A type cast. */ class unop_cast_operation : public maybe_constant_operation<operation_up, struct type *> diff --git a/gdb/stap-probe.c b/gdb/stap-probe.c index c4b24df..436ff4d 100644 --- a/gdb/stap-probe.c +++ b/gdb/stap-probe.c @@ -822,6 +822,21 @@ stap_parse_register_operand (struct stap_parse_info *p) operation_up reg = make_operation<register_operation> (std::move (regname)); + /* If the argument has been placed into a vector register then (for most + architectures), the type of this register will be a union of arrays. + As a result, attempting to cast from the register type to the scalar + argument type will not be possible (GDB will throw an error during + expression evaluation). + + The solution is to extract the scalar type from the value contents of + the entire register value. */ + if (!is_scalar_type (gdbarch_register_type (gdbarch, regnum))) + { + gdb_assert (is_scalar_type (p->arg_type)); + reg = make_operation<unop_extract_operation> (std::move (reg), + p->arg_type); + } + if (indirect_p) { if (disp_op != nullptr) diff --git a/gdb/std-operator.def b/gdb/std-operator.def index baaa594..5e6cad0 100644 --- a/gdb/std-operator.def +++ b/gdb/std-operator.def @@ -187,6 +187,15 @@ OP (OP_STRING) and they must all match. */ OP (OP_ARRAY) +/* UNOP_EXTRACT takes a value and a type, like a cast, but, instead of + casting the value to the given type, a new value (of the given + type) is extracted from the contents of the old value, starting + from the least significant byte. + + It is invalid for the given type to be larger than the type of the + given value. */ +OP (UNOP_EXTRACT) + /* UNOP_CAST is followed by a type pointer in the next exp_element. With another UNOP_CAST at the end, this makes three exp_elements. It casts the value of the following subexpression. */ diff --git a/gdb/testsuite/gdb.base/stap-probe.c b/gdb/testsuite/gdb.base/stap-probe.c index 2d13d02..d1482c3 100644 --- a/gdb/testsuite/gdb.base/stap-probe.c +++ b/gdb/testsuite/gdb.base/stap-probe.c @@ -29,6 +29,8 @@ __extension__ unsigned short test_m4_semaphore __attribute__ ((unused)) __attrib __extension__ unsigned short test_pstr_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); __extension__ unsigned short test_ps_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); + +__extension__ unsigned short test_xmmreg_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); #else int relocation_marker __attribute__ ((unused)); @@ -90,6 +92,24 @@ pstr (int val) return val == 0 ? a : b; } +#ifdef __SSE2__ +static const char * __attribute__((noinline)) +use_xmm_reg (int val) +{ + volatile register int val_in_reg asm ("xmm0") = val; + + STAP_PROBE1 (test, xmmreg, val_in_reg); + + return val == 0 ? "xxx" : "yyy"; +} +#else +static const char * __attribute__((noinline)) ATTRIBUTE_NOCLONE +use_xmm_reg (int val) +{ + /* Nothing. */ +} +#endif /* __SSE2__ */ + static void m4 (const struct funcs *fs, int v) { @@ -111,5 +131,7 @@ main() m4 (&fs, 0); m4 (&fs, 1); + use_xmm_reg (0x1234); + return 0; /* last break here */ } diff --git a/gdb/testsuite/gdb.base/stap-probe.exp b/gdb/testsuite/gdb.base/stap-probe.exp index e86c789..b0b690a 100644 --- a/gdb/testsuite/gdb.base/stap-probe.exp +++ b/gdb/testsuite/gdb.base/stap-probe.exp @@ -15,6 +15,98 @@ standard_testfile +# Count the number of probes of TYPE (either 'stap' or 'dtrace'), +# from provider matching PROVIDER, with a name matching NAME, and from +# an objec file matching OBJECT. +# +# The OBJECT is optional, in which case all objects will be matched. +# +# If any error condition is detected, then perror is called, and -1 +# returned. +# +# Otherwise, returns an integer, 0 or greater. +proc gdb_count_probes { type provider name { object "" }} { + set cmd "info probes ${type} ${provider} ${name}" + if { $object != "" } { + set cmd "$cmd ${object}" + } + + set probe_count 0 + set no_probes_line false + gdb_test_multiple $cmd "" { + -re "^$cmd\r\n" { + exp_continue + } + -re "^Type\\s+Provider\\s+Name\\s+Where\\s+Semaphore\\s+Object\\s*\r\n" { + exp_continue + } + -re "^\\s*\r\n" { + exp_continue + } + -re "^stap\[^\r\n\]+\r\n" { + incr probe_count + exp_continue + } + -re "^dtrace\[^\r\n\]+\r\n" { + incr probe_count + exp_continue + } + -re "^No probes matched\\.\r\n" { + set no_probes_line true + exp_continue + } + -re "^$::gdb_prompt $" { + pass $gdb_test_name + } + } + + if { [expr $no_probes_line && $probe_count > 0] \ + || [expr !$no_probes_line && $probe_count == 0] } { + perror "Mismatch between no probes found line, and probes count" + return -1 + } + + return $probe_count +} + +proc check_for_usable_xmm0_probe { binfile } { + set readelf_program [gdb_find_readelf] + set binfile [standard_output_file $binfile] + set command "exec $readelf_program -n $binfile" + verbose -log "command is $command" + set result [catch $command output] + verbose -log "result is $result" + verbose -log "output is $output" + + # We don't actually check RESULT. Sometimes readelf gives + # warnings about gaps in some of the notes data. This is + # unrelated to the staps probes, but still causes readelf to exit + # with non-zero status. + # + # Instead, just check the output. If readelf failed to run then + # the output will be empty, and the following regexps will fail to + # match. + + # First, look for the xmmreg probe, and if we find it, grab the + # argument string. + if ![regexp {\n\s+Provider: test\n\s+Name: xmmreg\n[^\n]+\n\s+Arguments: ([^\n]+)\n} $output ignore arguments] { + verbose -log "APB: Couldn't find probe at all" + return false + } + + verbose -log "APB: Matched on '$ignore'" + verbose -log "APB: arguments: '$arguments'" + + # Check the the argument string mentions xmm0. + if ![regexp {@%?xmm0} $arguments] { + verbose -log "APB: Prove doesn't use xmm0 register" + return false + } + + # Success! We have a probe that uses xmm0 for an argument. + return true +} + # Run the tests. We run the tests two different ways: once with a # plain probe, and once with a probe that has an associated semaphore. # This returns -1 on failure to compile or start, 0 otherwise. @@ -119,6 +211,24 @@ proc stap_test {exec_name {args ""}} { " = $hex .This is another test message.*" \ "print \$_probe_arg1 for probe ps" + # Check the probe is using the xmm0 register. + if [check_for_usable_xmm0_probe $exec_name] { + + delete_breakpoints + if {[runto "-pstap test:xmmreg"]} { + pass "run to -pstap test:xmmreg" + } else { + fail "run to -pstap test:xmmreg" + } + + gdb_test "print \$_probe_argc" " = 1" \ + "print \$_probe_argc for probe xmmreg" + gdb_test "print/x \$_probe_arg0" " = 0x1234" \ + "check \$_probe_arg0 for probe xmmreg" + } else { + unsupported "print probe argument from \$xmm0 register" + } + return 0 } @@ -185,6 +295,21 @@ proc stap_test_no_debuginfo {exec_name {args ""}} { " = $hex .This is another test message.*" \ "print \$_probe_arg1 for probe ps" + # Reinit GDB, set a breakpoint on probe ps. + if { [gdb_count_probes stap test xmmreg] > 0 } { + delete_breakpoints + if {[runto "-pstap test:xmmreg"]} { + pass "run to -pstap test:xmmreg" + } else { + fail "run to -pstap test:xmmreg" + } + + gdb_test "print \$_probe_argc" " = 1" \ + "print \$_probe_argc for probe xmmreg" + gdb_test "print/x \$_probe_arg0" " = 0x1234" \ + "check \$_probe_arg0 for probe xmmreg" + } + return 0 } |