diff --git a/src/stirling/obj_tools/testdata/cc/test_exe.cc b/src/stirling/obj_tools/testdata/cc/test_exe.cc index f5a39683fed..f8b61c98b8a 100644 --- a/src/stirling/obj_tools/testdata/cc/test_exe.cc +++ b/src/stirling/obj_tools/testdata/cc/test_exe.cc @@ -72,6 +72,17 @@ ABCStruct64 ABCSumMixed(ABCStruct32 x, ABCStruct64 y, int32_t z_a, int64_t z_b, return ABCStruct64{x.a + y.a + z_a + w.a, x.b + y.b + z_b + w.b, x.c + y.c + z_c + w.c}; } +void OuterStructFunc(OuterStruct x) { + x.O0++; + x.O1.M0.L0 = !x.O1.M0.L0; + x.O1.M0.L1++; + x.O1.M0.L2++; + x.O1.M1 = !x.O1.M1; + x.O1.M2.L0 = !x.O1.M2.L0; + x.O1.M2.L1++; + x.O1.M2.L2++; +} + void SomeFunctionWithPointerArgs(int* a, ABCStruct32* x) { x->a = *a; a++; @@ -113,6 +124,7 @@ int main() { sleep(1); } + OuterStructFunc(OuterStruct{1, MidStruct{{true, 2, nullptr}, false, {true, 3, nullptr}}}); return 0; } diff --git a/src/stirling/source_connectors/dynamic_tracer/BUILD.bazel b/src/stirling/source_connectors/dynamic_tracer/BUILD.bazel index 31ca423e95c..d604b6ed0e2 100644 --- a/src/stirling/source_connectors/dynamic_tracer/BUILD.bazel +++ b/src/stirling/source_connectors/dynamic_tracer/BUILD.bazel @@ -49,7 +49,9 @@ pl_cc_bpf_test( srcs = ["dynamic_trace_bpf_test.cc"], data = [ "//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_client:golang_1_16_grpc_client", + "//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_client:golang_1_21_grpc_client", "//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_server:golang_1_16_grpc_server_with_certs", + "//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_server:golang_1_21_grpc_server_with_certs", ], tags = [ "cpu:16", @@ -69,7 +71,7 @@ pl_cc_bpf_test( srcs = ["stirling_dt_bpf_test.cc"], data = [ "//src/stirling/obj_tools/testdata/cc:test_exe", - "//src/stirling/obj_tools/testdata/go:test_go_1_16_binary", + "//src/stirling/obj_tools/testdata/go:test_go_1_21_binary", "//src/stirling/testing/dns:dns_hammer", "//src/stirling/testing/dns:dns_hammer_image.tar", ], diff --git a/src/stirling/source_connectors/dynamic_tracer/dynamic_trace_bpf_test.cc b/src/stirling/source_connectors/dynamic_tracer/dynamic_trace_bpf_test.cc index 77c2588613d..05b1573cb04 100644 --- a/src/stirling/source_connectors/dynamic_tracer/dynamic_trace_bpf_test.cc +++ b/src/stirling/source_connectors/dynamic_tracer/dynamic_trace_bpf_test.cc @@ -32,11 +32,12 @@ constexpr std::string_view kClientPath = "src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_client/" - "golang_1_16_grpc_client"; + "golang_1_21_grpc_client"; constexpr std::string_view kServerPath = "src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_server/" - "golang_1_16_grpc_server"; + "golang_1_21_grpc_server"; +DECLARE_bool(debug_dt_pipeline); namespace px { namespace stirling { diff --git a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/BUILD.bazel b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/BUILD.bazel index 5df5b8015b0..998b1395468 100644 --- a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/BUILD.bazel +++ b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/BUILD.bazel @@ -45,7 +45,7 @@ pl_cc_test( name = "code_gen_test", srcs = ["code_gen_test.cc"], data = [ - "//src/stirling/obj_tools/testdata/go:test_go_1_16_binary", + "//src/stirling/obj_tools/testdata/go:test_go_1_21_binary", ], deps = [ ":cc_library", @@ -56,7 +56,8 @@ pl_cc_test( name = "dwarvifier_test", srcs = ["dwarvifier_test.cc"], data = [ - "//src/stirling/obj_tools/testdata/go:test_go_1_16_binary", + "//src/stirling/obj_tools/testdata/cc:test_exe", + "//src/stirling/obj_tools/testdata/go:test_go_1_21_binary", ], deps = [ ":cc_library", @@ -67,7 +68,7 @@ pl_cc_test( name = "probe_transformer_test", srcs = ["probe_transformer_test.cc"], data = [ - "//src/stirling/obj_tools/testdata/go:test_go_1_16_binary", + "//src/stirling/obj_tools/testdata/go:test_go_1_21_binary", ], deps = [ ":cc_library", @@ -79,7 +80,7 @@ pl_cc_test( name = "autogen_test", srcs = ["autogen_test.cc"], data = [ - "//src/stirling/obj_tools/testdata/go:test_go_1_16_binary", + "//src/stirling/obj_tools/testdata/go:test_go_1_21_binary", ], deps = [ ":cc_library", @@ -91,9 +92,9 @@ pl_cc_test( name = "dynamic_tracer_test", srcs = ["dynamic_tracer_test.cc"], data = [ - "//src/stirling/obj_tools/testdata/go:test_go_1_16_binary", - "//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_client:golang_1_16_grpc_client", - "//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_server:golang_1_16_grpc_server_with_certs", + "//src/stirling/obj_tools/testdata/go:test_go_1_21_binary", + "//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_client:golang_1_21_grpc_client", + "//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_server:golang_1_21_grpc_server_with_certs", ], tags = [ "exclusive", diff --git a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/autogen_test.cc b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/autogen_test.cc index 1b4699055e7..dbf07f112a1 100644 --- a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/autogen_test.cc +++ b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/autogen_test.cc @@ -22,7 +22,7 @@ #include "src/common/testing/testing.h" #include "src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/autogen.h" -constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_16_binary"; +constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_21_binary"; namespace px { namespace stirling { @@ -111,8 +111,8 @@ tracepoints { fields: "i1" fields: "i2" fields: "i3" - fields: "__tilde__r6" - fields: "__tilde__r7" + fields: "__tilde__r0" + fields: "__tilde__r1" fields: "latency" } probes { @@ -147,13 +147,15 @@ tracepoints { } ret_vals { id: "retval6" - expr: "~r6" + expr: "~r0" } ret_vals { id: "retval7" - expr: "~r7" + expr: "~r1" + } + function_latency { + id: "fn_latency" } - function_latency { id: "fn_latency" } output_actions { output_name: "main__d__MixedArgTypes_table" variable_names: "arg0" diff --git a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/code_gen_test.cc b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/code_gen_test.cc index 1c4671cf777..e1145550aad 100644 --- a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/code_gen_test.cc +++ b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/code_gen_test.cc @@ -22,7 +22,7 @@ #include "src/common/testing/testing.h" -constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_16_binary"; +constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_21_binary"; namespace px { namespace stirling { diff --git a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dwarvifier.cc b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dwarvifier.cc index 85ff8ab5184..480b9bc50d7 100644 --- a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dwarvifier.cc +++ b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dwarvifier.cc @@ -97,8 +97,9 @@ class Dwarvifier { uint64_t offset, const TypeInfo& type_info); // Used by ProcessVarExpr() to handle a Struct variable. - Status ProcessStructBlob(const std::string& base, uint64_t offset, const TypeInfo& type_info, - const std::string& var_name, ir::physical::Probe* output_probe); + Status ProcessStructBlob(const ArgInfo& arg_info, const std::string& base, uint64_t offset, + const TypeInfo& type_info, const std::string& var_name, + ir::physical::Probe* output_probe); // The input components describes a sequence of field of nesting structures. The first component // is the name of an input argument of a function, or an expression to describe the index of an @@ -569,11 +570,11 @@ void Dwarvifier::AddEntryProbeVariables(ir::physical::Probe* output_probe) { auto* parm_ptr_var = AddVariable(output_probe, kParmPtrVarName, ir::shared::VOID_POINTER); parm_ptr_var->set_reg(ir::physical::Register::SYSV_AMD64_ARGS_PTR); + } else if (language_ == ir::shared::GOLANG) { + auto* parm_ptr_var = + AddVariable(output_probe, kParmPtrVarName, ir::shared::VOID_POINTER); + parm_ptr_var->set_reg(ir::physical::Register::GOLANG_ARGS_PTR); } - // TODO(oazizi): For Golang 1.17+, will need the following: - // auto* parm_ptr_var = - // AddVariable(output_probe, kParmPtrVarName, ir::shared::VOID_POINTER); - // parm_ptr_var->set_reg(ir::physical::Register::GOLANG_ARGS_PTR); } void Dwarvifier::AddRetProbeVariables(ir::physical::Probe* output_probe) { @@ -585,6 +586,10 @@ void Dwarvifier::AddRetProbeVariables(ir::physical::Probe* output_probe) { auto* rc_ptr_var = AddVariable(output_probe, kRCPtrVarName, ir::shared::VOID_POINTER); rc_ptr_var->set_reg(ir::physical::Register::RC_PTR); + } else if (language_ == ir::shared::GOLANG) { + auto* parm_ptr_var = + AddVariable(output_probe, kParmPtrVarName, ir::shared::VOID_POINTER); + parm_ptr_var->set_reg(ir::physical::Register::GOLANG_ARGS_PTR); } } @@ -788,11 +793,22 @@ Status Dwarvifier::ProcessGolangInterfaceExpr(const std::string& base, uint64_t return Status::OK(); } -Status Dwarvifier::ProcessStructBlob(const std::string& base, uint64_t offset, - const TypeInfo& type_info, const std::string& var_name, +Status Dwarvifier::ProcessStructBlob(const ArgInfo& arg_info, const std::string& base, + uint64_t offset, const TypeInfo& type_info, + const std::string& var_name, ir::physical::Probe* output_probe) { PX_ASSIGN_OR_RETURN(std::vector struct_spec_entires, dwarf_reader_->GetStructSpec(type_info.type_name)); + VLOG(1) << arg_info.ToString(); + // TODO(ddelnano): Remove once structs in registers are supported (gh#2106) + if (arg_info.location.loc_type == LocationType::kRegister) { + auto message = absl::Substitute( + "Structs variables from registers aren't supported yet. Add an expr to '$0' to access an " + "individual, non struct field (e.g. expr: \"struct_name.field_a\") until this is supported " + "(gh#2106).", + var_name); + return error::InvalidArgument(message); + } PX_ASSIGN_OR_RETURN(ir::physical::StructSpec struct_spec_proto, CreateStructSpecProto(struct_spec_entires, language_)); @@ -917,7 +933,8 @@ Status Dwarvifier::ProcessVarExpr(const std::string& var_name, const ArgInfo& ar PX_RETURN_IF_ERROR( ProcessGolangInterfaceExpr(base, offset, type_info, var_name, output_probe)); } else { - PX_RETURN_IF_ERROR(ProcessStructBlob(base, offset, type_info, var_name, output_probe)); + PX_RETURN_IF_ERROR( + ProcessStructBlob(arg_info, base, offset, type_info, var_name, output_probe)); } } else { return error::Internal("Expected struct or base type, but got type: $0", type_info.ToString()); @@ -936,13 +953,23 @@ Status Dwarvifier::ProcessArgExpr(const ir::logical::Argument& arg, PX_ASSIGN_OR_RETURN(ArgInfo arg_info, GetArgInfo(args_map_, components.front())); + std::string base_var; switch (language_) { case ir::shared::GOLANG: - return ProcessVarExpr(arg.id(), arg_info, kSPVarName, components, output_probe); + switch (arg_info.location.loc_type) { + case LocationType::kStack: + base_var = kSPVarName; + break; + case LocationType::kRegister: + base_var = kParmPtrVarName; + break; + default: + return error::Internal("Unsupported argument LocationType $0", + magic_enum::enum_name(arg_info.location.loc_type)); + } + return ProcessVarExpr(arg.id(), arg_info, base_var, components, output_probe); case ir::shared::CPP: case ir::shared::C: { - std::string base_var; - switch (arg_info.location.loc_type) { case LocationType::kStack: base_var = kSPVarName; @@ -999,7 +1026,7 @@ Status Dwarvifier::ProcessRetValExpr(const ir::logical::ReturnValue& ret_val, switch (language_) { case ir::shared::GOLANG: { - // This represents the actualy return value being returned, + // This represents the actual return value being returned, // without sub-field accesses. std::string ret_val_name(components.front()); @@ -1029,8 +1056,15 @@ Status Dwarvifier::ProcessRetValExpr(const ir::logical::ReturnValue& ret_val, // Golang return values are really arguments located on the stack, so get the arg info. PX_ASSIGN_OR_RETURN(ArgInfo arg_info, GetArgInfo(args_map_, ret_val_name)); - - return ProcessVarExpr(ret_val.id(), arg_info, kSPVarName, components, output_probe); + switch (arg_info.location.loc_type) { + case LocationType::kStack: + return ProcessVarExpr(ret_val.id(), arg_info, kSPVarName, components, output_probe); + case LocationType::kRegister: + return ProcessVarExpr(ret_val.id(), arg_info, kParmPtrVarName, components, output_probe); + default: + return error::Internal("Unsupported return value LocationType $0", + magic_enum::enum_name(arg_info.location.loc_type)); + } } case ir::shared::CPP: case ir::shared::C: { diff --git a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dwarvifier_test.cc b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dwarvifier_test.cc index 6167e498fcc..27d540ea279 100644 --- a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dwarvifier_test.cc +++ b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dwarvifier_test.cc @@ -22,7 +22,8 @@ #include "src/common/testing/testing.h" #include "src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dwarvifier.h" -constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_16_binary"; +constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_21_binary"; +constexpr std::string_view kCPPBinaryPath = "src/stirling/obj_tools/testdata/cc/test_exe/test_exe"; namespace px { namespace stirling { @@ -128,13 +129,19 @@ probes { builtin: GOID } } + vars { + scalar_var { + name: "parm__" + type: VOID_POINTER + reg: GOLANG_ARGS_PTR + } + } vars { scalar_var { name: "arg0" type: INT memory { - base: "sp_" - offset: 8 + base: "parm__" } } } @@ -143,8 +150,8 @@ probes { name: "arg1" type: INT memory { - base: "sp_" - offset: 24 + base: "parm__" + offset: 48 } } } @@ -153,8 +160,8 @@ probes { name: "arg2" type: INT memory { - base: "sp_" - offset: 32 + base: "parm__" + offset: 56 } } } @@ -163,8 +170,8 @@ probes { name: "arg3" type: BOOL memory { - base: "sp_" - offset: 16 + base: "parm__" + offset: 8 } } } @@ -173,8 +180,8 @@ probes { name: "arg4" type: BOOL memory { - base: "sp_" - offset: 17 + base: "parm__" + offset: 16 } } } @@ -183,8 +190,8 @@ probes { name: "arg5" type: BOOL memory { - base: "sp_" - offset: 20 + base: "parm__" + offset: 19 } } } @@ -272,13 +279,19 @@ probes { builtin: GOID } } + vars { + scalar_var { + name: "parm__" + type: VOID_POINTER + reg: GOLANG_ARGS_PTR + } + } vars { scalar_var { name: "retval0" type: INT memory { - base: "sp_" - offset: 48 + base: "parm__" } } } @@ -287,8 +300,8 @@ probes { name: "retval1" type: BOOL memory { - base: "sp_" - offset: 57 + base: "parm__" + offset: 9 } } } @@ -311,11 +324,11 @@ tracepoints { } ret_vals { id: "retval0" - expr: "~r6" + expr: "~r0" } ret_vals { id: "retval1" - expr: "~r7.B1" + expr: "~r1.B1" } } } @@ -376,13 +389,19 @@ probes { builtin: GOID } } + vars { + scalar_var { + name: "parm__" + type: VOID_POINTER + reg: GOLANG_ARGS_PTR + } + } vars { scalar_var { name: "retval0" type: INT memory { - base: "sp_" - offset: 48 + base: "parm__" } } } @@ -391,8 +410,8 @@ probes { name: "retval1" type: BOOL memory { - base: "sp_" - offset: 57 + base: "parm__" + offset: 9 } } } @@ -480,13 +499,19 @@ probes { builtin: GOID } } + vars { + scalar_var { + name: "parm__" + type: VOID_POINTER + reg: GOLANG_ARGS_PTR + } + } vars { scalar_var { name: "retval0" type: INT memory { - base: "sp_" - offset: 48 + base: "parm__" } } } @@ -495,8 +520,8 @@ probes { name: "retval1" type: STRUCT_BLOB memory { - base: "sp_" - offset: 56 + base: "parm__" + offset: 8 size: 4 } } @@ -585,13 +610,20 @@ probes { builtin: GOID } } + vars { + scalar_var { + name: "parm__" + type: VOID_POINTER + reg: GOLANG_ARGS_PTR + } + } vars { scalar_var { name: "arg0_D_Ptr_X_" type: VOID_POINTER memory { - base: "sp_" - offset: 16 + base: "parm__" + offset: 8 } } } @@ -619,8 +651,8 @@ probes { name: "arg1_D_Ptr_X_" type: VOID_POINTER memory { - base: "sp_" - offset: 16 + base: "parm__" + offset: 8 } } } @@ -860,13 +892,19 @@ probes { builtin: GOID } } + vars { + scalar_var { + name: "parm__" + type: VOID_POINTER + reg: GOLANG_ARGS_PTR + } + } vars { scalar_var { name: "arg0" type: INT memory { - base: "sp_" - offset: 8 + base: "parm__" } } } @@ -875,8 +913,8 @@ probes { name: "arg1" type: BOOL memory { - base: "sp_" - offset: 16 + base: "parm__" + offset: 8 } } } @@ -885,8 +923,8 @@ probes { name: "arg2" type: BOOL memory { - base: "sp_" - offset: 17 + base: "parm__" + offset: 16 } } } @@ -971,6 +1009,13 @@ probes { builtin: GOID } } + vars { + scalar_var { + name: "parm__" + type: VOID_POINTER + reg: GOLANG_ARGS_PTR + } + } vars { map_var { name: "my_stash_ptr" @@ -1003,7 +1048,6 @@ probes { } output_actions { perf_buffer_name: "out_table2" - data_buffer_array_name: "out_table2_data_buffer_array" output_struct_name: "out_table2_value_t" variable_names: "tgid_" variable_names: "tgid_start_time_" @@ -1011,6 +1055,7 @@ probes { variable_names: "goid_" variable_names: "arg0" variable_names: "arg1" + data_buffer_array_name: "out_table2_data_buffer_array" } map_delete_actions { map_name: "my_stash" @@ -1195,13 +1240,19 @@ probes { builtin: GOID } } + vars { + scalar_var { + name: "parm__" + type: VOID_POINTER + reg: GOLANG_ARGS_PTR + } + } vars { scalar_var { name: "arg0" type: STRUCT_BLOB memory { - base: "sp_" - offset: 8 + base: "parm__" size: 48 } } @@ -1227,6 +1278,352 @@ arrays { } )"; +constexpr std::string_view kCPPStackStructProbeIn = R"( +deployment_spec { + path_list { + paths: "$0" + } +} +tracepoints { + program { + language: CPP + outputs { + name: "out_table" + fields: "out" + } + probes { + tracepoint: { + symbol: "OuterStructFunc" + type: ENTRY + } + args { + id: "arg0" + expr: "x" + } + output_actions { + output_name: "out_table" + variable_names: "arg0" + } + } + } +} +)"; + +constexpr std::string_view kCPPStackStructProbeOut = R"( +deployment_spec { + path_list { + paths: "$0" + } +} +structs { + name: "out_table_value_t" + fields { + name: "tgid_" + type: INT32 + } + fields { + name: "tgid_start_time_" + type: UINT64 + } + fields { + name: "time_" + type: UINT64 + } + fields { + name: "out" + type: STRUCT_BLOB + blob_decoders { + entries { + size: 8 + type: LONG + path: "/O0" + } + entries { + offset: 8 + size: 1 + type: BOOL + path: "/O1/M0/L0" + } + entries { + offset: 12 + size: 4 + type: INT + path: "/O1/M0/L1" + } + entries { + offset: 16 + size: 8 + type: VOID_POINTER + path: "/O1/M0/L2" + } + entries { + offset: 24 + size: 1 + type: BOOL + path: "/O1/M1" + } + entries { + offset: 32 + size: 1 + type: BOOL + path: "/O1/M2/L0" + } + entries { + offset: 36 + size: 4 + type: INT + path: "/O1/M2/L1" + } + entries { + offset: 40 + size: 8 + type: VOID_POINTER + path: "/O1/M2/L2" + } + } + } +} +outputs { + name: "out_table" + fields: "out" + struct_type: "out_table_value_t" +} +probes { + tracepoint { + symbol: "OuterStructFunc" + type: ENTRY + } + vars { + scalar_var { + name: "sp_" + type: VOID_POINTER + reg: SP + } + } + vars { + scalar_var { + name: "tgid_" + type: INT32 + builtin: TGID + } + } + vars { + scalar_var { + name: "tgid_pid_" + type: UINT64 + builtin: TGID_PID + } + } + vars { + scalar_var { + name: "tgid_start_time_" + type: UINT64 + builtin: TGID_START_TIME + } + } + vars { + scalar_var { + name: "time_" + type: UINT64 + builtin: KTIME + } + } + vars { + scalar_var { + name: "parm__" + type: VOID_POINTER + reg: SYSV_AMD64_ARGS_PTR + } + } + vars { + scalar_var { + name: "arg0" + type: STRUCT_BLOB + memory { + base: "sp_" + offset: 8 + size: 48 + } + } + } + output_actions { + perf_buffer_name: "out_table" + data_buffer_array_name: "out_table_data_buffer_array" + output_struct_name: "out_table_value_t" + variable_names: "tgid_" + variable_names: "tgid_start_time_" + variable_names: "time_" + variable_names: "arg0" + } +} +language: CPP +arrays { + name: "out_table_data_buffer_array" + type { + struct_type: "out_table_value_t" + } + capacity: 1 +} +)"; + +constexpr std::string_view kCPPRegStructProbeIn = R"( +deployment_spec { + path_list { + paths: "$0" + } +} +tracepoints { + program { + language: CPP + outputs { + name: "out_table" + fields: "out" + } + probes { + tracepoint: { + symbol: "ABCSumMixed" + type: ENTRY + } + args { + id: "arg0" + expr: "x" + } + output_actions { + output_name: "out_table" + variable_names: "arg0" + } + } + } +} +)"; + +constexpr std::string_view kCPPRegStructProbeOut = R"( +deployment_spec { + path_list { + paths: "$0" + } +} +structs { + name: "out_table_value_t" + fields { + name: "tgid_" + type: INT32 + } + fields { + name: "tgid_start_time_" + type: UINT64 + } + fields { + name: "time_" + type: UINT64 + } + fields { + name: "out" + type: STRUCT_BLOB + blob_decoders { + entries { + size: 8 + type: LONG + path: "/a" + } + entries { + offset: 8 + size: 8 + type: LONG + path: "/b + } + entries { + offset: 16 + size: 8 + type: LONG + path: "/c" + } + } + } +} +outputs { + name: "out_table" + fields: "out" + struct_type: "out_table_value_t" +} +probes { + tracepoint { + symbol: "ABCSumMixed" + type: ENTRY + } + vars { + scalar_var { + name: "sp_" + type: VOID_POINTER + reg: SP + } + } + vars { + scalar_var { + name: "tgid_" + type: INT32 + builtin: TGID + } + } + vars { + scalar_var { + name: "tgid_pid_" + type: UINT64 + builtin: TGID_PID + } + } + vars { + scalar_var { + name: "tgid_start_time_" + type: UINT64 + builtin: TGID_START_TIME + } + } + vars { + scalar_var { + name: "time_" + type: UINT64 + builtin: KTIME + } + } + vars { + scalar_var { + name: "parm__" + type: VOID_POINTER + reg: SYSV_AMD64_ARGS_PTR + } + } + vars { + scalar_var { + name: "arg0" + type: STRUCT_BLOB + memory { + base: "sp_" + offset: 8 + size: 48 + } + } + } + output_actions { + perf_buffer_name: "out_table" + data_buffer_array_name: "out_table_data_buffer_array" + output_struct_name: "out_table_value_t" + variable_names: "tgid_" + variable_names: "tgid_start_time_" + variable_names: "time_" + variable_names: "arg0" + } +} +language: CPP +arrays { + name: "out_table_data_buffer_array" + type { + struct_type: "out_table_value_t" + } + capacity: 1 +} +)"; + constexpr std::string_view kGolangErrorInterfaceProbeIn = R"( deployment_spec { path_list { @@ -1324,6 +1721,8 @@ structs { path: "/len" } } + blob_decoders { + } } } outputs { @@ -1378,13 +1777,19 @@ probes { builtin: GOID } } + vars { + scalar_var { + name: "parm__" + type: VOID_POINTER + reg: GOLANG_ARGS_PTR + } + } vars { scalar_var { name: "retval_intf_tab" type: UINT64 memory { - base: "sp_" - offset: 8 + base: "parm__" } } } @@ -1393,8 +1798,8 @@ probes { name: "retval_intf_data" type: VOID_POINTER memory { - base: "sp_" - offset: 16 + base: "parm__" + offset: 8 } } } @@ -1402,14 +1807,21 @@ probes { scalar_var { name: "main__IntStruct_sym_addr1" type: UINT64 - constant: "5104328" + constant: "4989600" } } vars { scalar_var { name: "runtime__errorString_sym_addr2" type: UINT64 - constant: "5104360" + constant: "4989792" + } + } + vars { + scalar_var { + name: "internal___poll__errNetClosing_sym_addr3" + type: UINT64 + constant: "4989824" } } vars { @@ -1426,8 +1838,7 @@ probes { name: "retval" type: STRUCT_BLOB memory { - base: "sp_" - offset: 8 + base: "parm__" size: 16 op: ASSIGN_ONLY } @@ -1435,13 +1846,13 @@ probes { } output_actions { perf_buffer_name: "out_table" - data_buffer_array_name: "out_table_data_buffer_array" output_struct_name: "out_table_value_t" variable_names: "tgid_" variable_names: "tgid_start_time_" variable_names: "time_" variable_names: "goid_" variable_names: "retval" + data_buffer_array_name: "out_table_data_buffer_array" } cond_blocks { cond { @@ -1481,6 +1892,25 @@ probes { } } } + cond_blocks { + cond { + op: EQUAL + vars: "retval_intf_tab" + vars: "internal___poll__errNetClosing_sym_addr3" + } + vars { + scalar_var { + name: "retval" + type: STRUCT_BLOB + memory { + base: "retval_intf_data" + size: 16 + decoder_idx: 3 + op: ASSIGN_ONLY + } + } + } + } } language: GOLANG arrays { @@ -1493,31 +1923,37 @@ arrays { )"; struct DwarfInfoTestParam { + std::string_view binary_path; std::string_view input; std::string_view expected_output; + std::string_view error_message = ""; }; -class DwarfInfoTest : public ::testing::TestWithParam { - protected: - DwarfInfoTest() : binary_path_(px::testing::BazelRunfilePath(kBinaryPath)) {} - - std::string binary_path_; -}; +class DwarfInfoTest : public ::testing::TestWithParam {}; TEST_P(DwarfInfoTest, Transform) { using obj_tools::DwarfReader; using obj_tools::ElfReader; DwarfInfoTestParam p = GetParam(); + std::string binary_path = px::testing::BazelRunfilePath(p.binary_path); - std::string input_str = absl::Substitute(p.input, binary_path_); + std::string input_str = absl::Substitute(p.input, binary_path); ir::logical::TracepointDeployment input_program; ASSERT_TRUE(TextFormat::ParseFromString(std::string(input_str), &input_program)); - std::string expected_output_str = absl::Substitute(p.expected_output, binary_path_); + std::string expected_output_str = absl::Substitute(p.expected_output, binary_path); ASSERT_OK_AND_ASSIGN(std::unique_ptr dwarf_reader, - DwarfReader::CreateIndexingAll(binary_path_)); - ASSERT_OK_AND_ASSIGN(std::unique_ptr elf_reader, ElfReader::Create(binary_path_)); + DwarfReader::CreateIndexingAll(binary_path)); + ASSERT_OK_AND_ASSIGN(std::unique_ptr elf_reader, ElfReader::Create(binary_path)); + + if (!p.error_message.empty()) { + auto status = GeneratePhysicalProgram(input_program, dwarf_reader.get(), elf_reader.get()); + EXPECT_FALSE(status.ok()); + EXPECT_THAT(status.msg(), ::testing::HasSubstr(p.error_message)); + return; + } + ASSERT_OK_AND_ASSIGN( ir::physical::Program physical_program, GeneratePhysicalProgram(input_program, dwarf_reader.get(), elf_reader.get())); @@ -1535,17 +1971,23 @@ TEST_P(DwarfInfoTest, Transform) { #endif } +constexpr std::string_view kStructRegErrorPrefix = + "Structs variables from registers aren't supported yet"; INSTANTIATE_TEST_SUITE_P( DwarfInfoTestSuite, DwarfInfoTest, - ::testing::Values(DwarfInfoTestParam{kEntryProbeIn, kEntryProbeOut}, - DwarfInfoTestParam{kReturnProbeIn, kReturnProbeOut}, - DwarfInfoTestParam{kImplicitNamedRetvalsIn, kImplicitNamedRetvalsOut}, - DwarfInfoTestParam{kNamedRetvalsIn, kNamedRetvalsOut}, - DwarfInfoTestParam{kNestedArgProbeIn, kNestedArgProbeOut}, - DwarfInfoTestParam{kActionProbeIn, kActionProbeOut}, - DwarfInfoTestParam{kStructProbeIn, kStructProbeOut}, - DwarfInfoTestParam{kGolangErrorInterfaceProbeIn, - kGolangErrorInterfaceProbeOut})); + ::testing::Values( + DwarfInfoTestParam{kBinaryPath, kEntryProbeIn, kEntryProbeOut}, + DwarfInfoTestParam{kBinaryPath, kReturnProbeIn, kReturnProbeOut}, + DwarfInfoTestParam{kBinaryPath, kImplicitNamedRetvalsIn, kImplicitNamedRetvalsOut}, + DwarfInfoTestParam{kBinaryPath, kNamedRetvalsIn, kNamedRetvalsOut, kStructRegErrorPrefix}, + DwarfInfoTestParam{kBinaryPath, kNestedArgProbeIn, kNestedArgProbeOut}, + DwarfInfoTestParam{kBinaryPath, kActionProbeIn, kActionProbeOut}, + DwarfInfoTestParam{kBinaryPath, kStructProbeIn, kStructProbeOut, kStructRegErrorPrefix}, + DwarfInfoTestParam{kCPPBinaryPath, kCPPStackStructProbeIn, kCPPStackStructProbeOut}, + DwarfInfoTestParam{kCPPBinaryPath, kCPPRegStructProbeIn, kCPPRegStructProbeOut, + kStructRegErrorPrefix}, + DwarfInfoTestParam{kBinaryPath, kGolangErrorInterfaceProbeIn, + kGolangErrorInterfaceProbeOut})); } // namespace dynamic_tracing } // namespace stirling diff --git a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dynamic_tracer_test.cc b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dynamic_tracer_test.cc index 5d0305ae200..4d27a922c95 100644 --- a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dynamic_tracer_test.cc +++ b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dynamic_tracer_test.cc @@ -26,7 +26,7 @@ #include "src/common/testing/testing.h" #include "src/stirling/testing/common.h" -constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_16_binary"; +constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_21_binary"; namespace px { namespace stirling { @@ -45,7 +45,7 @@ using ::testing::SizeIs; constexpr char kClientPath[] = "src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_client/" - "golang_1_16_grpc_client"; + "golang_1_21_grpc_client"; constexpr char kClientPathExpected[] = "src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_client/" @@ -53,7 +53,7 @@ constexpr char kClientPathExpected[] = constexpr char kServerPath[] = "src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_server/" - "golang_1_16_grpc_server"; + "golang_1_21_grpc_server"; constexpr char kServerPathExpected[] = "src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_server/" @@ -347,7 +347,7 @@ const std::vector kExpectedBCC = { " uint8_t truncated;", "};", "struct pid_goid_map_value_t {", - " int64_t goid;", + " uint64_t goid;", "} __attribute__((packed, aligned(1)));", "struct probe0_argstash_value_t {", " int arg0;", @@ -382,13 +382,16 @@ const std::vector kExpectedBCC = { "uint64_t tgid_start_time_ = pl_tgid_start_time();", "uint64_t time_ = bpf_ktime_get_ns();", "int64_t goid_ = pl_goid();", + "uint64_t parm___[9];parm___[0] = ctx->ax;parm___[1] = ctx->bx;parm___[2] = ctx->cx;parm___[3] " + "= ctx->di;parm___[4] = ctx->si;parm___[5] = ctx->r8;parm___[6] = ctx->r9;parm___[7] = " + "ctx->r10;parm___[8] = ctx->r11;void* parm__ = &parm___;", "int64_t kGRunningState = 2;", "void* goid_X_;", - "bpf_probe_read(&goid_X_, sizeof(void*), sp_ + 8);", - "int64_t goid;", - "bpf_probe_read(&goid, sizeof(int64_t), goid_X_ + 152);", + "bpf_probe_read(&goid_X_, sizeof(void*), parm__ + 0);", + "uint64_t goid;", + "bpf_probe_read(&goid, sizeof(uint64_t), goid_X_ + 152);", "uint32_t newval;", - "bpf_probe_read(&newval, sizeof(uint32_t), sp_ + 20);", + "bpf_probe_read(&newval, sizeof(uint32_t), parm__ + 16);", "struct pid_goid_map_value_t pid_goid_map_value = {};", "pid_goid_map_value.goid = goid;", "if (newval == kGRunningState) {", @@ -403,12 +406,15 @@ const std::vector kExpectedBCC = { "uint64_t tgid_start_time_ = pl_tgid_start_time();", "uint64_t time_ = bpf_ktime_get_ns();", "int64_t goid_ = pl_goid();", + "uint64_t parm___[9];parm___[0] = ctx->ax;parm___[1] = ctx->bx;parm___[2] = ctx->cx;parm___[3] " + "= ctx->di;parm___[4] = ctx->si;parm___[5] = ctx->r8;parm___[6] = ctx->r9;parm___[7] = " + "ctx->r10;parm___[8] = ctx->r11;void* parm__ = &parm___;", "int arg0;", - "bpf_probe_read(&arg0, sizeof(int), sp_ + 8);", + "bpf_probe_read(&arg0, sizeof(int), parm__ + 0);", "int arg1;", - "bpf_probe_read(&arg1, sizeof(int), sp_ + 24);", + "bpf_probe_read(&arg1, sizeof(int), parm__ + 48);", "int arg2;", - "bpf_probe_read(&arg2, sizeof(int), sp_ + 32);", + "bpf_probe_read(&arg2, sizeof(int), parm__ + 56);", "struct probe0_argstash_value_t probe0_argstash_value = {};", "probe0_argstash_value.arg0 = arg0;", "probe0_argstash_value.arg1 = arg1;", @@ -424,8 +430,11 @@ const std::vector kExpectedBCC = { "uint64_t tgid_start_time_ = pl_tgid_start_time();", "uint64_t time_ = bpf_ktime_get_ns();", "int64_t goid_ = pl_goid();", + "uint64_t parm___[9];parm___[0] = ctx->ax;parm___[1] = ctx->bx;parm___[2] = ctx->cx;parm___[3] " + "= ctx->di;parm___[4] = ctx->si;parm___[5] = ctx->r8;parm___[6] = ctx->r9;parm___[7] = " + "ctx->r10;parm___[8] = ctx->r11;void* parm__ = &parm___;", "int retval0;", - "bpf_probe_read(&retval0, sizeof(int), sp_ + 48);", + "bpf_probe_read(&retval0, sizeof(int), parm__ + 0);", "struct probe0_argstash_value_t* probe0_argstash_ptr = probe0_argstash.lookup(&goid_);", "if (probe0_argstash_ptr == NULL) { return 0; }", "int arg0 = probe0_argstash_ptr->arg0;", @@ -467,7 +476,7 @@ TEST(DynamicTracerTest, Compile) { const auto& spec = bcc_program.uprobe_specs[0]; - EXPECT_THAT(spec, Field(&UProbeSpec::binary_path, ::testing::EndsWith("test_go_1_16_binary"))); + EXPECT_THAT(spec, Field(&UProbeSpec::binary_path, ::testing::EndsWith("test_go_1_21_binary"))); EXPECT_THAT(spec, Field(&UProbeSpec::symbol, "runtime.casgstatus")); EXPECT_THAT(spec, Field(&UProbeSpec::attach_type, bpf_tools::BPFProbeAttachType::kEntry)); EXPECT_THAT(spec, Field(&UProbeSpec::probe_fn, "probe_entry_runtime_casgstatus")); diff --git a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/probe_transformer_test.cc b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/probe_transformer_test.cc index e0cfd05e762..f10217815cb 100644 --- a/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/probe_transformer_test.cc +++ b/src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/probe_transformer_test.cc @@ -21,7 +21,7 @@ #include "src/common/testing/testing.h" #include "src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/probe_transformer.h" -constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_16_binary"; +constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_21_binary"; namespace px { namespace stirling { @@ -212,7 +212,14 @@ tracepoints { symbol: "main.MixedArgTypes" type: RETURN } - function_latency { id: "fn_latency" } + ret_vals { + id: "retval0" + expr: "$$0" + } + ret_vals { + id: "retval1" + expr: "$$1" + } map_vals { map_name: "probe0_argstash" key: GOID @@ -224,13 +231,8 @@ tracepoints { value_ids: "arg5" value_ids: "start_ktime_ns" } - ret_vals { - id: "retval0" - expr: "$$0" - } - ret_vals { - id: "retval1" - expr: "$$1" + function_latency { + id: "fn_latency" } output_actions { output_name: "probe0_table" diff --git a/src/stirling/source_connectors/dynamic_tracer/stirling_dt_bpf_test.cc b/src/stirling/source_connectors/dynamic_tracer/stirling_dt_bpf_test.cc index 3c00ddb1c40..f159bd64105 100644 --- a/src/stirling/source_connectors/dynamic_tracer/stirling_dt_bpf_test.cc +++ b/src/stirling/source_connectors/dynamic_tracer/stirling_dt_bpf_test.cc @@ -277,7 +277,7 @@ TEST_F(DynamicTraceAPITest, InvalidReference) { class DynamicTraceGolangTest : public StirlingDynamicTraceBPFTest { protected: const std::string kBinaryPath = - BazelRunfilePath("src/stirling/obj_tools/testdata/go/test_go_1_16_binary"); + BazelRunfilePath("src/stirling/obj_tools/testdata/go/test_go_1_21_binary"); }; TEST_F(DynamicTraceGolangTest, TraceLatencyOnly) { @@ -430,7 +430,9 @@ TEST_F(DynamicTraceGolangTest, TraceLongString) { // variable into BPF_PERCPU_ARRAY: // (https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md#7-bpf_percpu_array) // to work around the stack size limit. -TEST_F(DynamicTraceGolangTest, TraceStructBlob) { +// TODO(ddelnano): Re-enable once Struct variable types can be resolved from registers (gh#2106). +// STRUCT_BLOB type assumes the struct exists in a packed format in memory. +TEST_F(DynamicTraceGolangTest, DISABLED_TraceStructBlob) { BinaryRunner trace_target; trace_target.Run(kBinaryPath); @@ -703,6 +705,54 @@ TEST_F(DynamicTraceCppTest, BasicTypes) { EXPECT_EQ(rb[sum_field_idx]->Get(0).val, 7); } +TEST_F(DynamicTraceCppTest, TraceStructBlob) { + BinaryRunner trace_target; + trace_target.Run(kBinaryPath); + + constexpr std::string_view kProgramTxtPB = R"( + deployment_spec { + path_list { + paths: "$0" + } + } + tracepoints { + program { + language: CPP + outputs { + name: "output_table" + fields: "struct_blob" + } + probes { + name: "probe0" + tracepoint { + symbol: "OuterStructFunc" + type: LOGICAL + } + args { + id: "arg0" + expr: "x" + } + output_actions { + output_name: "output_table" + variable_names: "arg0" + } + } + } + } + )"; + + auto trace_program = Prepare(kProgramTxtPB, kBinaryPath); + DeployTracepoint(std::move(trace_program)); + + ASSERT_HAS_VALUE_AND_ASSIGN(int struct_blob_field_idx, + FindFieldIndex(info_class_.schema(), "struct_blob")); + + types::ColumnWrapperRecordBatch& rb = *record_batches_[0]; + EXPECT_EQ( + rb[struct_blob_field_idx]->Get(0), + R"({"O0":1,"O1":{"M0":{"L0":true,"L1":2,"L2":0},"M1":false,"M2":{"L0":true,"L1":3,"L2":0}}})"); +} + class DynamicTraceCppTestWithParam : public DynamicTraceCppTest, public ::testing::WithParamInterface {};