Skip to content

Commit 548ee06

Browse files
committed
Add GoOffsetLocator that fulfills DwarfReader APIs
Signed-off-by: Dom Del Nano <ddelnano@gmail.com>
1 parent 3a7e3fe commit 548ee06

2 files changed

Lines changed: 216 additions & 7 deletions

File tree

src/stirling/source_connectors/socket_tracer/uprobe_symaddrs.h

Lines changed: 125 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818

1919
#pragma once
2020

21+
#include <map>
22+
#include <memory>
2123
#include <string>
2224

2325
#include "src/common/base/base.h"
2426
#include "src/stirling/obj_tools/dwarf_reader.h"
2527
#include "src/stirling/obj_tools/elf_reader.h"
28+
#include "src/stirling/obj_tools/go_syms.h"
2629
#include "src/stirling/obj_tools/raw_fptr_manager.h"
2730
#include "src/stirling/source_connectors/socket_tracer/bcc_bpf_intf/common.h"
2831
#include "src/stirling/source_connectors/socket_tracer/bcc_bpf_intf/symaddrs.h"
@@ -36,26 +39,144 @@ namespace stirling {
3639

3740
constexpr std::string_view kLibNettyTcnativePrefix = "libnetty_tcnative_linux_x86";
3841

42+
using StructOffsetMap =
43+
std::map<std::string, std::map<std::string, std::map<std::string, int32_t>>>;
44+
using FunctionArgMap =
45+
std::map<std::string,
46+
std::map<std::string, std::map<std::string, std::unique_ptr<obj_tools::VarLocation>>>>;
47+
48+
class GoOffsetLocator {
49+
public:
50+
// TODO(ddelnano): Remove this constructor once the scaffolding to populate StructOffsetMap
51+
// and FunctionArgMap is available.
52+
GoOffsetLocator(obj_tools::DwarfReader* dwarf_reader, const obj_tools::BuildInfo& build_info,
53+
const std::string& go_version)
54+
: GoOffsetLocator(dwarf_reader, StructOffsetMap{}, FunctionArgMap{}, build_info, go_version) {
55+
}
56+
57+
GoOffsetLocator(obj_tools::DwarfReader* dwarf_reader, const StructOffsetMap& struct_offsets,
58+
const FunctionArgMap& function_args, const obj_tools::BuildInfo& build_info,
59+
const std::string& go_version)
60+
: dwarf_reader_(dwarf_reader),
61+
struct_offsets_(struct_offsets),
62+
function_args_(function_args),
63+
go_version_(go_version) {
64+
PopulateModuleVersions(build_info);
65+
}
66+
67+
StatusOr<std::map<std::string, obj_tools::ArgInfo>> GetFunctionArgInfo(
68+
std::string_view function_symbol_name) {
69+
if (dwarf_reader_ != nullptr) {
70+
return dwarf_reader_->GetFunctionArgInfo(function_symbol_name);
71+
}
72+
return GetFunctionArgInfoFromOffsets(function_symbol_name);
73+
}
74+
75+
StatusOr<obj_tools::VarLocation> GetArgumentLocation(std::string_view /*function_symbol_name*/,
76+
std::string_view /*arg_name*/) {
77+
return error::Internal(
78+
"GetArgumentLocation is not implemented for GoOffsetLocator. Use GetFunctionArgInfo "
79+
"instead.");
80+
}
81+
82+
StatusOr<uint64_t> GetStructMemberOffset(std::string_view struct_name,
83+
std::string_view member_name) {
84+
if (dwarf_reader_ != nullptr) {
85+
return dwarf_reader_->GetStructMemberOffset(struct_name, member_name);
86+
}
87+
return GetStructMemberOffsetFromOffsets(struct_name, member_name);
88+
}
89+
90+
private:
91+
StatusOr<std::map<std::string, obj_tools::ArgInfo>> GetFunctionArgInfoFromOffsets(
92+
std::string_view function_symbol_name) {
93+
auto fn_map = function_args_.find(std::string(function_symbol_name));
94+
if (fn_map == function_args_.end()) {
95+
return error::Internal("Unable to find function location for $0", function_symbol_name);
96+
}
97+
std::map<std::string, obj_tools::ArgInfo> result;
98+
for (const auto& [arg_name, version_info_map] : fn_map->second) {
99+
std::string version_key = go_version_;
100+
auto version_map = version_info_map.find(version_key);
101+
if (version_map == version_info_map.end()) {
102+
return error::Internal("Unable to find function location for arg=$0 version=$1", arg_name,
103+
version_key);
104+
}
105+
auto var_loc_ptr = version_map->second.get();
106+
if (var_loc_ptr == nullptr) {
107+
return error::Internal("Function location for arg=$0 version=$1 is missing", arg_name,
108+
version_key);
109+
}
110+
result.insert({arg_name, obj_tools::ArgInfo{obj_tools::TypeInfo{}, *var_loc_ptr}});
111+
}
112+
return result;
113+
}
114+
115+
StatusOr<uint64_t> GetStructMemberOffsetFromOffsets(std::string_view struct_name,
116+
std::string_view member_name) {
117+
auto struct_map = struct_offsets_.find(std::string(struct_name));
118+
if (struct_map == struct_offsets_.end()) {
119+
return error::Internal("Unable to find offsets for struct=$0", struct_name);
120+
}
121+
auto member_map = struct_map->second.find(std::string(member_name));
122+
if (member_map == struct_map->second.end()) {
123+
return error::Internal("Unable to find offsets for struct member=$0.$1", struct_name,
124+
member_name);
125+
}
126+
127+
std::string version_key = go_version_;
128+
auto version_map = member_map->second.find(version_key);
129+
if (version_map == member_map->second.end()) {
130+
return error::Internal("Unable to find offsets for struct member=$0.$1 for version $2",
131+
struct_name, member_name, version_key);
132+
}
133+
return version_map->second;
134+
}
135+
136+
void PopulateModuleVersions(const obj_tools::BuildInfo& build_info) {
137+
for (const auto& dep : build_info.deps) {
138+
// Find the related dependencies and strip the "v" prefix
139+
if (dep.path == "golang.org/x/net") {
140+
golang_x_net_version_ = dep.version.substr(1);
141+
} else if (dep.path == "google.golang.org/grpc") {
142+
google_golang_grpc_version_ = dep.version.substr(1);
143+
}
144+
}
145+
VLOG(1) << "golang.org/x/net module version: " << golang_x_net_version_;
146+
VLOG(1) << "google.golang.org/grpc module version: " << google_golang_grpc_version_;
147+
}
148+
149+
obj_tools::DwarfReader* dwarf_reader_;
150+
151+
const StructOffsetMap& struct_offsets_;
152+
const FunctionArgMap& function_args_;
153+
154+
const std::string& go_version_;
155+
156+
std::string golang_x_net_version_;
157+
std::string google_golang_grpc_version_;
158+
};
159+
39160
/**
40161
* Uses ELF and DWARF information to return the locations of all relevant symbols for general Go
41162
* uprobe deployment.
42163
*/
43164
StatusOr<struct go_common_symaddrs_t> GoCommonSymAddrs(obj_tools::ElfReader* elf_reader,
44-
obj_tools::DwarfReader* dwarf_reader);
165+
GoOffsetLocator* offset_locator);
45166

46167
/**
47168
* Uses ELF and DWARF information to return the locations of all relevant symbols for Go HTTP2
48169
* uprobe deployment.
49170
*/
50171
StatusOr<struct go_http2_symaddrs_t> GoHTTP2SymAddrs(obj_tools::ElfReader* elf_reader,
51-
obj_tools::DwarfReader* dwarf_reader);
172+
GoOffsetLocator* offset_locator);
52173

53174
/**
54175
* Uses ELF and DWARF information to return the locations of all relevant symbols for Go TLS
55176
* uprobe deployment.
56177
*/
57178
StatusOr<struct go_tls_symaddrs_t> GoTLSSymAddrs(obj_tools::ElfReader* elf_reader,
58-
obj_tools::DwarfReader* dwarf_reader);
179+
GoOffsetLocator* offset_locator);
59180

60181
/**
61182
* Detects the version of OpenSSL to return the locations of all relevant symbols for OpenSSL uprobe
@@ -74,7 +195,7 @@ StatusOr<struct node_tlswrap_symaddrs_t> NodeTLSWrapSymAddrs(const std::filesyst
74195
const SemVer& ver);
75196

76197
px::Status PopulateGoTLSDebugSymbols(obj_tools::ElfReader* elf_reader,
77-
obj_tools::DwarfReader* dwarf_reader,
198+
GoOffsetLocator* offset_locator,
78199
struct go_tls_symaddrs_t* symaddrs);
79200

80201
} // namespace stirling

src/stirling/source_connectors/socket_tracer/uprobe_symaddrs_test.cc

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,22 @@ class UprobeSymaddrsTest : public ::testing::Test {
4040
void SetUp() {
4141
std::filesystem::path p = px::testing::BazelRunfilePath(kGoGRPCServer);
4242
ASSERT_OK_AND_ASSIGN(dwarf_reader_, DwarfReader::CreateIndexingAll(p));
43+
offset_locator_ = std::make_unique<GoOffsetLocator>(
44+
GoOffsetLocator(dwarf_reader_.get(), {}, {}, {}, "1.19.0"));
4345
ASSERT_OK_AND_ASSIGN(elf_reader_, ElfReader::Create(p));
4446
}
4547

4648
static inline constexpr std::string_view kGoGRPCServer =
4749
"src/stirling/testing/demo_apps/go_grpc_tls_pl/server/golang_1_19_grpc_tls_server_binary";
4850

4951
std::unique_ptr<DwarfReader> dwarf_reader_;
52+
std::unique_ptr<GoOffsetLocator> offset_locator_;
5053
std::unique_ptr<ElfReader> elf_reader_;
5154
};
5255

5356
TEST_F(UprobeSymaddrsTest, GoCommonSymAddrs) {
5457
ASSERT_OK_AND_ASSIGN(struct go_common_symaddrs_t symaddrs,
55-
GoCommonSymAddrs(elf_reader_.get(), dwarf_reader_.get()));
58+
GoCommonSymAddrs(elf_reader_.get(), offset_locator_.get()));
5659

5760
// Check a few interface types.
5861
EXPECT_NE(symaddrs.tls_Conn, -1);
@@ -68,7 +71,7 @@ TEST_F(UprobeSymaddrsTest, GoCommonSymAddrs) {
6871

6972
TEST_F(UprobeSymaddrsTest, GoHTTP2SymAddrs) {
7073
ASSERT_OK_AND_ASSIGN(struct go_http2_symaddrs_t symaddrs,
71-
GoHTTP2SymAddrs(elf_reader_.get(), dwarf_reader_.get()));
74+
GoHTTP2SymAddrs(elf_reader_.get(), offset_locator_.get()));
7275

7376
// Check a few interface types.
7477
EXPECT_NE(symaddrs.transport_bufWriter, -1);
@@ -85,7 +88,7 @@ TEST_F(UprobeSymaddrsTest, GoHTTP2SymAddrs) {
8588

8689
TEST_F(UprobeSymaddrsTest, GoTLSSymAddrs) {
8790
ASSERT_OK_AND_ASSIGN(struct go_tls_symaddrs_t symaddrs,
88-
GoTLSSymAddrs(elf_reader_.get(), dwarf_reader_.get()));
91+
GoTLSSymAddrs(elf_reader_.get(), offset_locator_.get()));
8992

9093
// Check some member offsets.
9194
// The values may change when golang version is updated.
@@ -110,5 +113,90 @@ TEST(UprobeSymaddrsNodeTest, TLSWrapSymAddrsFromDwarfInfo) {
110113
EXPECT_EQ(symaddrs.uv__io_s_fd_offset, 0x04);
111114
}
112115

116+
TEST(GoOffsetLocator, GetStructMemberOffsetNoDWARFMissingStruct) {
117+
auto offset_locator = GoOffsetLocator(nullptr, {}, {}, {}, "");
118+
auto status = offset_locator.GetStructMemberOffset("runtime.g", "goid");
119+
EXPECT_FALSE(status.ok());
120+
}
121+
122+
// NOLINTNEXTLINE: runtime/string
123+
std::string go_version = "1.19.0";
124+
auto struct_map = StructOffsetMap{{"runtime.g", {{"goid", {{go_version, 152}}}}}};
125+
126+
TEST(GoOffsetLocator, GetStructMemberOffsetNoDWARFMissingMember) {
127+
auto offset_locator = GoOffsetLocator(nullptr, struct_map, {}, {}, go_version);
128+
EXPECT_FALSE(offset_locator.GetStructMemberOffset("runtime.g", "missing_member").ok());
129+
}
130+
131+
TEST(GoOffsetLocator, GetStructMemberOffsetNoDWARFMissingVersion) {
132+
auto offset_locator = GoOffsetLocator(nullptr, struct_map, {}, {}, "1.18.0");
133+
EXPECT_FALSE(offset_locator.GetStructMemberOffset("runtime.g", "goid").ok());
134+
}
135+
136+
TEST(GoOffsetLocator, GetStructMemberOffsetNoDWARFSuccessfulLookup) {
137+
auto offset_locator = GoOffsetLocator(nullptr, struct_map, {}, {}, go_version);
138+
139+
ASSERT_OK_AND_ASSIGN(uint64_t offset, offset_locator.GetStructMemberOffset("runtime.g", "goid"));
140+
EXPECT_EQ(offset, 152);
141+
}
142+
143+
TEST(GoOffsetLocator, GetFunctionArgInfoNoDWARFMissingFunction) {
144+
auto offset_locator = GoOffsetLocator(nullptr, {}, {}, {}, "");
145+
auto status = offset_locator.GetFunctionArgInfo("missing_func");
146+
EXPECT_FALSE(status.ok());
147+
}
148+
149+
TEST(GoOffsetLocator, GetFunctionArgInfoNoDWARFMissingVersion) {
150+
FunctionArgMap fn_arg_map;
151+
auto var_location = obj_tools::VarLocation{obj_tools::LocationType::kRegister, 8};
152+
fn_arg_map["crypto/tls.(*Conn).Read"]["b"][go_version] =
153+
std::make_unique<obj_tools::VarLocation>(var_location);
154+
155+
auto offset_locator = GoOffsetLocator(nullptr, struct_map, fn_arg_map, {}, "1.18.0");
156+
auto status = offset_locator.GetFunctionArgInfo("crypto/tls.(*Conn).Write");
157+
EXPECT_FALSE(status.ok());
158+
}
159+
160+
TEST(GoOffsetLocator, GetStructMemberOffsetNoDWARFUnknownLocation) {
161+
FunctionArgMap fn_arg_map;
162+
auto var_location = obj_tools::VarLocation{obj_tools::LocationType::kUnknown, -1};
163+
fn_arg_map["crypto/tls.(*Conn).Read"]["b"][go_version] =
164+
std::make_unique<obj_tools::VarLocation>(var_location);
165+
166+
auto offset_locator = GoOffsetLocator(nullptr, struct_map, fn_arg_map, {}, go_version);
167+
168+
auto status = offset_locator.GetFunctionArgInfo("crypto/tls.(*Conn).Read");
169+
ASSERT_OK_AND_ASSIGN(auto args, offset_locator.GetFunctionArgInfo("crypto/tls.(*Conn).Read"));
170+
EXPECT_NE(args.find("b"), args.end());
171+
auto& arg_info = args["b"];
172+
EXPECT_EQ(arg_info.location.loc_type, obj_tools::LocationType::kUnknown);
173+
EXPECT_EQ(arg_info.location.offset, -1);
174+
}
175+
176+
TEST(GoOffsetLocator, GetStructMemberOffsetNoDWARFNullLocation) {
177+
FunctionArgMap fn_arg_map;
178+
fn_arg_map["crypto/tls.(*Conn).Read"]["b"][go_version] = nullptr;
179+
180+
auto offset_locator = GoOffsetLocator(nullptr, struct_map, fn_arg_map, {}, go_version);
181+
182+
auto status = offset_locator.GetFunctionArgInfo("crypto/tls.(*Conn).Read");
183+
EXPECT_FALSE(status.ok());
184+
}
185+
186+
TEST(GoOffsetLocator, GetFunctionArgInfoNoDWARFSuccessfulLookup) {
187+
FunctionArgMap fn_arg_map;
188+
auto var_location = obj_tools::VarLocation{obj_tools::LocationType::kRegister, 8};
189+
fn_arg_map["crypto/tls.(*Conn).Read"]["b"][go_version] =
190+
std::make_unique<obj_tools::VarLocation>(var_location);
191+
192+
auto offset_locator = GoOffsetLocator(nullptr, struct_map, fn_arg_map, {}, go_version);
193+
194+
ASSERT_OK_AND_ASSIGN(auto args, offset_locator.GetFunctionArgInfo("crypto/tls.(*Conn).Read"));
195+
EXPECT_NE(args.find("b"), args.end());
196+
auto& arg_info = args["b"];
197+
EXPECT_EQ(arg_info.location.loc_type, obj_tools::LocationType::kRegister);
198+
EXPECT_EQ(arg_info.location.offset, 8);
199+
}
200+
113201
} // namespace stirling
114202
} // namespace px

0 commit comments

Comments
 (0)