Skip to content

Commit 8232eee

Browse files
authored
Update dynamic logging to work with Go's 1.17+ calling convention (#2108)
Summary: Update dynamic logging to work with Go's 1.17+ calling convention Despite our earlier messaging that dynamic logging would be deprecated in #2105, I decided that it was easier to handle the new ABI instead of deprecating the Go parts of the dynamic tracer. My rationale for this is that the blocker for achieving feature parity for the new Go ABI, handling `STRUCT_BLOB` variables, is actually a concern for c/c++ tracepoint programs as well. I felt it was important to keep the dynamic tracer rather than deprecate it entirely. As mentioned above, this change does not have full feature parity with the legacy ABI since `STRUCT_BLOB` variables don't work with register based calling conventions -- these variables copy a blob of memory with the assumption that a packed struct exists in a contiguous chunk of memory. However, this isn't unique to Go binaries as c/c++ applications can pass certain structs via [registers](https://github.com/pixie-io/pixie/blob/b0001dab6288508295c54f36c81b3ed80c2677e1/src/stirling/obj_tools/testdata/cc/test_exe.cc#L66-L69) as well. This change replaces the previous Go `STRUCT_BLOB` tests with C equivalents that pass the variable on the stack. When a struct variable will be passed via a register, dynamic logging will now return an error and suggest to the user to access struct fields individually. This error and the test cases that assert the error is returned can be reverted to their original form once #2106 is complete. Relevant Issues: #2105 Type of change: /kind cleanup Test Plan: Existing tests pass and new ones were added where coverage was needed Changelog Message: Update Go dynamic logging feature to support Go 1.17+ binaries --------- Signed-off-by: Dom Del Nano <[email protected]>
1 parent b84bf6d commit 8232eee

11 files changed

+679
-124
lines changed

src/stirling/obj_tools/testdata/cc/test_exe.cc

+12
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ ABCStruct64 ABCSumMixed(ABCStruct32 x, ABCStruct64 y, int32_t z_a, int64_t z_b,
7272
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};
7373
}
7474

75+
void OuterStructFunc(OuterStruct x) {
76+
x.O0++;
77+
x.O1.M0.L0 = !x.O1.M0.L0;
78+
x.O1.M0.L1++;
79+
x.O1.M0.L2++;
80+
x.O1.M1 = !x.O1.M1;
81+
x.O1.M2.L0 = !x.O1.M2.L0;
82+
x.O1.M2.L1++;
83+
x.O1.M2.L2++;
84+
}
85+
7586
void SomeFunctionWithPointerArgs(int* a, ABCStruct32* x) {
7687
x->a = *a;
7788
a++;
@@ -113,6 +124,7 @@ int main() {
113124

114125
sleep(1);
115126
}
127+
OuterStructFunc(OuterStruct{1, MidStruct{{true, 2, nullptr}, false, {true, 3, nullptr}}});
116128

117129
return 0;
118130
}

src/stirling/source_connectors/dynamic_tracer/BUILD.bazel

+3-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ pl_cc_bpf_test(
4949
srcs = ["dynamic_trace_bpf_test.cc"],
5050
data = [
5151
"//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_client:golang_1_16_grpc_client",
52+
"//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_client:golang_1_21_grpc_client",
5253
"//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_server:golang_1_16_grpc_server_with_certs",
54+
"//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_server:golang_1_21_grpc_server_with_certs",
5355
],
5456
tags = [
5557
"cpu:16",
@@ -69,7 +71,7 @@ pl_cc_bpf_test(
6971
srcs = ["stirling_dt_bpf_test.cc"],
7072
data = [
7173
"//src/stirling/obj_tools/testdata/cc:test_exe",
72-
"//src/stirling/obj_tools/testdata/go:test_go_1_16_binary",
74+
"//src/stirling/obj_tools/testdata/go:test_go_1_21_binary",
7375
"//src/stirling/testing/dns:dns_hammer",
7476
"//src/stirling/testing/dns:dns_hammer_image.tar",
7577
],

src/stirling/source_connectors/dynamic_tracer/dynamic_trace_bpf_test.cc

+3-2
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@
3232

3333
constexpr std::string_view kClientPath =
3434
"src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_client/"
35-
"golang_1_16_grpc_client";
35+
"golang_1_21_grpc_client";
3636
constexpr std::string_view kServerPath =
3737
"src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_server/"
38-
"golang_1_16_grpc_server";
38+
"golang_1_21_grpc_server";
3939

40+
DECLARE_bool(debug_dt_pipeline);
4041
namespace px {
4142
namespace stirling {
4243

src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/BUILD.bazel

+8-7
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pl_cc_test(
4545
name = "code_gen_test",
4646
srcs = ["code_gen_test.cc"],
4747
data = [
48-
"//src/stirling/obj_tools/testdata/go:test_go_1_16_binary",
48+
"//src/stirling/obj_tools/testdata/go:test_go_1_21_binary",
4949
],
5050
deps = [
5151
":cc_library",
@@ -56,7 +56,8 @@ pl_cc_test(
5656
name = "dwarvifier_test",
5757
srcs = ["dwarvifier_test.cc"],
5858
data = [
59-
"//src/stirling/obj_tools/testdata/go:test_go_1_16_binary",
59+
"//src/stirling/obj_tools/testdata/cc:test_exe",
60+
"//src/stirling/obj_tools/testdata/go:test_go_1_21_binary",
6061
],
6162
deps = [
6263
":cc_library",
@@ -67,7 +68,7 @@ pl_cc_test(
6768
name = "probe_transformer_test",
6869
srcs = ["probe_transformer_test.cc"],
6970
data = [
70-
"//src/stirling/obj_tools/testdata/go:test_go_1_16_binary",
71+
"//src/stirling/obj_tools/testdata/go:test_go_1_21_binary",
7172
],
7273
deps = [
7374
":cc_library",
@@ -79,7 +80,7 @@ pl_cc_test(
7980
name = "autogen_test",
8081
srcs = ["autogen_test.cc"],
8182
data = [
82-
"//src/stirling/obj_tools/testdata/go:test_go_1_16_binary",
83+
"//src/stirling/obj_tools/testdata/go:test_go_1_21_binary",
8384
],
8485
deps = [
8586
":cc_library",
@@ -91,9 +92,9 @@ pl_cc_test(
9192
name = "dynamic_tracer_test",
9293
srcs = ["dynamic_tracer_test.cc"],
9394
data = [
94-
"//src/stirling/obj_tools/testdata/go:test_go_1_16_binary",
95-
"//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_client:golang_1_16_grpc_client",
96-
"//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_server:golang_1_16_grpc_server_with_certs",
95+
"//src/stirling/obj_tools/testdata/go:test_go_1_21_binary",
96+
"//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_client:golang_1_21_grpc_client",
97+
"//src/stirling/source_connectors/socket_tracer/protocols/http2/testing/go_grpc_server:golang_1_21_grpc_server_with_certs",
9798
],
9899
tags = [
99100
"exclusive",

src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/autogen_test.cc

+8-6
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
#include "src/common/testing/testing.h"
2323
#include "src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/autogen.h"
2424

25-
constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_16_binary";
25+
constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_21_binary";
2626

2727
namespace px {
2828
namespace stirling {
@@ -111,8 +111,8 @@ tracepoints {
111111
fields: "i1"
112112
fields: "i2"
113113
fields: "i3"
114-
fields: "__tilde__r6"
115-
fields: "__tilde__r7"
114+
fields: "__tilde__r0"
115+
fields: "__tilde__r1"
116116
fields: "latency"
117117
}
118118
probes {
@@ -147,13 +147,15 @@ tracepoints {
147147
}
148148
ret_vals {
149149
id: "retval6"
150-
expr: "~r6"
150+
expr: "~r0"
151151
}
152152
ret_vals {
153153
id: "retval7"
154-
expr: "~r7"
154+
expr: "~r1"
155+
}
156+
function_latency {
157+
id: "fn_latency"
155158
}
156-
function_latency { id: "fn_latency" }
157159
output_actions {
158160
output_name: "main__d__MixedArgTypes_table"
159161
variable_names: "arg0"

src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/code_gen_test.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
#include "src/common/testing/testing.h"
2424

25-
constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_16_binary";
25+
constexpr std::string_view kBinaryPath = "src/stirling/obj_tools/testdata/go/test_go_1_21_binary";
2626

2727
namespace px {
2828
namespace stirling {

src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/dwarvifier.cc

+49-15
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,9 @@ class Dwarvifier {
9797
uint64_t offset, const TypeInfo& type_info);
9898

9999
// Used by ProcessVarExpr() to handle a Struct variable.
100-
Status ProcessStructBlob(const std::string& base, uint64_t offset, const TypeInfo& type_info,
101-
const std::string& var_name, ir::physical::Probe* output_probe);
100+
Status ProcessStructBlob(const ArgInfo& arg_info, const std::string& base, uint64_t offset,
101+
const TypeInfo& type_info, const std::string& var_name,
102+
ir::physical::Probe* output_probe);
102103

103104
// The input components describes a sequence of field of nesting structures. The first component
104105
// 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) {
569570
auto* parm_ptr_var =
570571
AddVariable<ScalarVariable>(output_probe, kParmPtrVarName, ir::shared::VOID_POINTER);
571572
parm_ptr_var->set_reg(ir::physical::Register::SYSV_AMD64_ARGS_PTR);
573+
} else if (language_ == ir::shared::GOLANG) {
574+
auto* parm_ptr_var =
575+
AddVariable<ScalarVariable>(output_probe, kParmPtrVarName, ir::shared::VOID_POINTER);
576+
parm_ptr_var->set_reg(ir::physical::Register::GOLANG_ARGS_PTR);
572577
}
573-
// TODO(oazizi): For Golang 1.17+, will need the following:
574-
// auto* parm_ptr_var =
575-
// AddVariable<ScalarVariable>(output_probe, kParmPtrVarName, ir::shared::VOID_POINTER);
576-
// parm_ptr_var->set_reg(ir::physical::Register::GOLANG_ARGS_PTR);
577578
}
578579

579580
void Dwarvifier::AddRetProbeVariables(ir::physical::Probe* output_probe) {
@@ -585,6 +586,10 @@ void Dwarvifier::AddRetProbeVariables(ir::physical::Probe* output_probe) {
585586
auto* rc_ptr_var =
586587
AddVariable<ScalarVariable>(output_probe, kRCPtrVarName, ir::shared::VOID_POINTER);
587588
rc_ptr_var->set_reg(ir::physical::Register::RC_PTR);
589+
} else if (language_ == ir::shared::GOLANG) {
590+
auto* parm_ptr_var =
591+
AddVariable<ScalarVariable>(output_probe, kParmPtrVarName, ir::shared::VOID_POINTER);
592+
parm_ptr_var->set_reg(ir::physical::Register::GOLANG_ARGS_PTR);
588593
}
589594
}
590595

@@ -788,11 +793,22 @@ Status Dwarvifier::ProcessGolangInterfaceExpr(const std::string& base, uint64_t
788793
return Status::OK();
789794
}
790795

791-
Status Dwarvifier::ProcessStructBlob(const std::string& base, uint64_t offset,
792-
const TypeInfo& type_info, const std::string& var_name,
796+
Status Dwarvifier::ProcessStructBlob(const ArgInfo& arg_info, const std::string& base,
797+
uint64_t offset, const TypeInfo& type_info,
798+
const std::string& var_name,
793799
ir::physical::Probe* output_probe) {
794800
PX_ASSIGN_OR_RETURN(std::vector<StructSpecEntry> struct_spec_entires,
795801
dwarf_reader_->GetStructSpec(type_info.type_name));
802+
VLOG(1) << arg_info.ToString();
803+
// TODO(ddelnano): Remove once structs in registers are supported (gh#2106)
804+
if (arg_info.location.loc_type == LocationType::kRegister) {
805+
auto message = absl::Substitute(
806+
"Structs variables from registers aren't supported yet. Add an expr to '$0' to access an "
807+
"individual, non struct field (e.g. expr: \"struct_name.field_a\") until this is supported "
808+
"(gh#2106).",
809+
var_name);
810+
return error::InvalidArgument(message);
811+
}
796812
PX_ASSIGN_OR_RETURN(ir::physical::StructSpec struct_spec_proto,
797813
CreateStructSpecProto(struct_spec_entires, language_));
798814

@@ -917,7 +933,8 @@ Status Dwarvifier::ProcessVarExpr(const std::string& var_name, const ArgInfo& ar
917933
PX_RETURN_IF_ERROR(
918934
ProcessGolangInterfaceExpr(base, offset, type_info, var_name, output_probe));
919935
} else {
920-
PX_RETURN_IF_ERROR(ProcessStructBlob(base, offset, type_info, var_name, output_probe));
936+
PX_RETURN_IF_ERROR(
937+
ProcessStructBlob(arg_info, base, offset, type_info, var_name, output_probe));
921938
}
922939
} else {
923940
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,
936953

937954
PX_ASSIGN_OR_RETURN(ArgInfo arg_info, GetArgInfo(args_map_, components.front()));
938955

956+
std::string base_var;
939957
switch (language_) {
940958
case ir::shared::GOLANG:
941-
return ProcessVarExpr(arg.id(), arg_info, kSPVarName, components, output_probe);
959+
switch (arg_info.location.loc_type) {
960+
case LocationType::kStack:
961+
base_var = kSPVarName;
962+
break;
963+
case LocationType::kRegister:
964+
base_var = kParmPtrVarName;
965+
break;
966+
default:
967+
return error::Internal("Unsupported argument LocationType $0",
968+
magic_enum::enum_name(arg_info.location.loc_type));
969+
}
970+
return ProcessVarExpr(arg.id(), arg_info, base_var, components, output_probe);
942971
case ir::shared::CPP:
943972
case ir::shared::C: {
944-
std::string base_var;
945-
946973
switch (arg_info.location.loc_type) {
947974
case LocationType::kStack:
948975
base_var = kSPVarName;
@@ -999,7 +1026,7 @@ Status Dwarvifier::ProcessRetValExpr(const ir::logical::ReturnValue& ret_val,
9991026

10001027
switch (language_) {
10011028
case ir::shared::GOLANG: {
1002-
// This represents the actualy return value being returned,
1029+
// This represents the actual return value being returned,
10031030
// without sub-field accesses.
10041031
std::string ret_val_name(components.front());
10051032

@@ -1029,8 +1056,15 @@ Status Dwarvifier::ProcessRetValExpr(const ir::logical::ReturnValue& ret_val,
10291056

10301057
// Golang return values are really arguments located on the stack, so get the arg info.
10311058
PX_ASSIGN_OR_RETURN(ArgInfo arg_info, GetArgInfo(args_map_, ret_val_name));
1032-
1033-
return ProcessVarExpr(ret_val.id(), arg_info, kSPVarName, components, output_probe);
1059+
switch (arg_info.location.loc_type) {
1060+
case LocationType::kStack:
1061+
return ProcessVarExpr(ret_val.id(), arg_info, kSPVarName, components, output_probe);
1062+
case LocationType::kRegister:
1063+
return ProcessVarExpr(ret_val.id(), arg_info, kParmPtrVarName, components, output_probe);
1064+
default:
1065+
return error::Internal("Unsupported return value LocationType $0",
1066+
magic_enum::enum_name(arg_info.location.loc_type));
1067+
}
10341068
}
10351069
case ir::shared::CPP:
10361070
case ir::shared::C: {

0 commit comments

Comments
 (0)