Skip to content

Commit 5b20e35

Browse files
authored
Add GoOffsetLocator that fulfills DwarfReader API. Use as shim for Go Uprobes (#2207)
Summary: Add `GoOffsetLocator` that fulfills DwarfReader APIs. Use as shim for Go Uprobes Our current Go uprobe implementation relies on parsing DWARF information. This is memory intensive and is not ideal if end users want to keep the PEM's memory usage low (results in 100-150MB memory spikes). In order to support lower memory uprobes and to support Go binaries without DWARF, we can introduce a shim for the existing DwarfReader API that can read the offsets from a file (via openteletrmy-go-instrumentation generated offsets) or from the existing DWARF information. This PR introduces the `GoOffsetLocator` and tests that it functions properly while leaving the DWARF based tracing in place. The next set of changes will populate the offsets from pixie-io/opentelemetry-go-instrumentation#1 and provide configuration to choose the desired implementation: solely DWARF, solely static offsets or to use them in tandem. Relevant Issues: N/A Type of change: /kind feature Test Plan: Existing Go bpf trace tests should pass --------- Signed-off-by: Dom Del Nano <[email protected]>
1 parent cce2254 commit 5b20e35

File tree

6 files changed

+359
-89
lines changed

6 files changed

+359
-89
lines changed

src/stirling/binaries/go_binary_parse_profiling.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
#include "src/stirling/source_connectors/socket_tracer/uprobe_symaddrs.h"
2222

2323
using px::StatusOr;
24+
using px::stirling::GoOffsetLocator;
2425
using px::stirling::PopulateGoTLSDebugSymbols;
2526
using px::stirling::obj_tools::DwarfReader;
2627
using px::stirling::obj_tools::ElfReader;
28+
using px::stirling::obj_tools::ReadGoBuildInfo;
2729

2830
//-----------------------------------------------------------------------------
2931
// This utility is designed to isolate parsing the debug symbols of a Go binary. This
@@ -62,8 +64,15 @@ int main(int argc, char** argv) {
6264
}
6365
std::unique_ptr<DwarfReader> dwarf_reader = dwarf_reader_status.ConsumeValueOrDie();
6466

67+
auto build_info_s = ReadGoBuildInfo(elf_reader.get());
68+
const auto& [go_version, build_info] = build_info_s.ConsumeValueOrDie();
69+
px::stirling::StructOffsetMap struct_offsets;
70+
px::stirling::FunctionArgMap function_args;
71+
std::unique_ptr<GoOffsetLocator> go_offset_locator = std::make_unique<GoOffsetLocator>(
72+
dwarf_reader.get(), struct_offsets, function_args, build_info, go_version);
73+
6574
struct go_tls_symaddrs_t symaddrs;
66-
auto status = PopulateGoTLSDebugSymbols(elf_reader.get(), dwarf_reader.get(), &symaddrs);
75+
auto status = PopulateGoTLSDebugSymbols(go_offset_locator.get(), &symaddrs);
6776

6877
if (!status.ok()) {
6978
LOG(ERROR) << absl::Substitute("debug symbol parsing failed with: $0", status.msg());

src/stirling/source_connectors/socket_tracer/uprobe_manager.cc

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,10 @@ Status UProbeManager::UpdateOpenSSLSymAddrs(obj_tools::RawFptrManager* fptr_mana
174174
return Status::OK();
175175
}
176176

177-
Status UProbeManager::UpdateGoCommonSymAddrs(ElfReader* elf_reader, DwarfReader* dwarf_reader,
177+
Status UProbeManager::UpdateGoCommonSymAddrs(ElfReader* elf_reader, GoOffsetLocator* offset_locator,
178178
const std::vector<int32_t>& pids) {
179179
PX_ASSIGN_OR_RETURN(struct go_common_symaddrs_t symaddrs,
180-
GoCommonSymAddrs(elf_reader, dwarf_reader));
180+
GoCommonSymAddrs(elf_reader, offset_locator));
181181

182182
for (auto& pid : pids) {
183183
PX_RETURN_IF_ERROR(go_common_symaddrs_map_->SetValue(pid, symaddrs));
@@ -186,10 +186,10 @@ Status UProbeManager::UpdateGoCommonSymAddrs(ElfReader* elf_reader, DwarfReader*
186186
return Status::OK();
187187
}
188188

189-
Status UProbeManager::UpdateGoHTTP2SymAddrs(ElfReader* elf_reader, DwarfReader* dwarf_reader,
189+
Status UProbeManager::UpdateGoHTTP2SymAddrs(ElfReader* elf_reader, GoOffsetLocator* offset_locator,
190190
const std::vector<int32_t>& pids) {
191191
PX_ASSIGN_OR_RETURN(struct go_http2_symaddrs_t symaddrs,
192-
GoHTTP2SymAddrs(elf_reader, dwarf_reader));
192+
GoHTTP2SymAddrs(elf_reader, offset_locator));
193193

194194
for (auto& pid : pids) {
195195
PX_RETURN_IF_ERROR(go_http2_symaddrs_map_->SetValue(pid, symaddrs));
@@ -198,9 +198,9 @@ Status UProbeManager::UpdateGoHTTP2SymAddrs(ElfReader* elf_reader, DwarfReader*
198198
return Status::OK();
199199
}
200200

201-
Status UProbeManager::UpdateGoTLSSymAddrs(ElfReader* elf_reader, DwarfReader* dwarf_reader,
201+
Status UProbeManager::UpdateGoTLSSymAddrs(GoOffsetLocator* offset_locator,
202202
const std::vector<int32_t>& pids) {
203-
PX_ASSIGN_OR_RETURN(struct go_tls_symaddrs_t symaddrs, GoTLSSymAddrs(elf_reader, dwarf_reader));
203+
PX_ASSIGN_OR_RETURN(struct go_tls_symaddrs_t symaddrs, GoTLSSymAddrs(offset_locator));
204204

205205
for (auto& pid : pids) {
206206
PX_RETURN_IF_ERROR(go_tls_symaddrs_map_->SetValue(pid, symaddrs));
@@ -524,10 +524,10 @@ StatusOr<int> UProbeManager::AttachNodeJsOpenSSLUprobes(const uint32_t pid,
524524

525525
StatusOr<int> UProbeManager::AttachGoTLSUProbes(const std::string& binary,
526526
obj_tools::ElfReader* elf_reader,
527-
obj_tools::DwarfReader* dwarf_reader,
527+
GoOffsetLocator* offset_locator,
528528
const std::vector<int32_t>& pids) {
529529
// Step 1: Update BPF symbols_map on all new PIDs.
530-
Status s = UpdateGoTLSSymAddrs(elf_reader, dwarf_reader, pids);
530+
Status s = UpdateGoTLSSymAddrs(offset_locator, pids);
531531
if (!s.ok()) {
532532
// Doesn't appear to be a binary with the mandatory symbols.
533533
// Might not even be a golang binary.
@@ -546,10 +546,10 @@ StatusOr<int> UProbeManager::AttachGoTLSUProbes(const std::string& binary,
546546

547547
StatusOr<int> UProbeManager::AttachGoHTTP2UProbes(const std::string& binary,
548548
obj_tools::ElfReader* elf_reader,
549-
obj_tools::DwarfReader* dwarf_reader,
549+
GoOffsetLocator* offset_locator,
550550
const std::vector<int32_t>& pids) {
551551
// Step 1: Update BPF symaddrs for this binary.
552-
Status s = UpdateGoHTTP2SymAddrs(elf_reader, dwarf_reader, pids);
552+
Status s = UpdateGoHTTP2SymAddrs(elf_reader, offset_locator, pids);
553553
if (!s.ok()) {
554554
return 0;
555555
}
@@ -886,8 +886,30 @@ int UProbeManager::DeployGoUProbes(const absl::flat_hash_set<md::UPID>& pids) {
886886
binary, dwarf_reader_status.msg());
887887
continue;
888888
}
889+
890+
auto build_info_s = ReadGoBuildInfo(elf_reader.get());
891+
obj_tools::BuildInfo build_info;
892+
std::string go_version;
893+
if (build_info_s.ok()) {
894+
auto& build_info_pair = build_info_s.ValueOrDie();
895+
go_version = build_info_pair.first;
896+
build_info = std::move(build_info_pair.second);
897+
} else {
898+
VLOG(1) << absl::Substitute("Failed to read build info from binary $0. Message = $1", binary,
899+
build_info_s.status().msg());
900+
901+
continue;
902+
}
903+
889904
std::unique_ptr<DwarfReader> dwarf_reader = dwarf_reader_status.ConsumeValueOrDie();
890-
Status s = UpdateGoCommonSymAddrs(elf_reader.get(), dwarf_reader.get(), pid_vec);
905+
906+
// TODO(ddelnano): The struct and function offsets will be populated by the
907+
// next set of changes.
908+
StructOffsetMap struct_offsets;
909+
FunctionArgMap function_offsets;
910+
std::unique_ptr<GoOffsetLocator> offset_locator = std::make_unique<GoOffsetLocator>(
911+
dwarf_reader.get(), struct_offsets, function_offsets, build_info, go_version);
912+
Status s = UpdateGoCommonSymAddrs(elf_reader.get(), offset_locator.get(), pid_vec);
891913
if (!s.ok()) {
892914
VLOG(1) << absl::Substitute(
893915
"Golang binary $0 does not have the mandatory symbols (e.g. TCPConn).", binary);
@@ -898,7 +920,7 @@ int UProbeManager::DeployGoUProbes(const absl::flat_hash_set<md::UPID>& pids) {
898920
if (!cfg_disable_go_tls_tracing_) {
899921
VLOG(1) << absl::Substitute("Attempting to attach Go TLS uprobes to binary $0", binary);
900922
StatusOr<int> attach_status =
901-
AttachGoTLSUProbes(binary, elf_reader.get(), dwarf_reader.get(), pid_vec);
923+
AttachGoTLSUProbes(binary, elf_reader.get(), offset_locator.get(), pid_vec);
902924
if (!attach_status.ok()) {
903925
monitor_.AppendSourceStatusRecord("socket_tracer", attach_status.status(),
904926
"AttachGoTLSUProbes");
@@ -912,7 +934,7 @@ int UProbeManager::DeployGoUProbes(const absl::flat_hash_set<md::UPID>& pids) {
912934
// Go HTTP2 Probes.
913935
if (!cfg_disable_go_tls_tracing_ && cfg_enable_http2_tracing_) {
914936
StatusOr<int> attach_status =
915-
AttachGoHTTP2UProbes(binary, elf_reader.get(), dwarf_reader.get(), pid_vec);
937+
AttachGoHTTP2UProbes(binary, elf_reader.get(), offset_locator.get(), pid_vec);
916938
if (!attach_status.ok()) {
917939
monitor_.AppendSourceStatusRecord("socket_tracer", attach_status.status(),
918940
"AttachGoHTTP2UProbes");

src/stirling/source_connectors/socket_tracer/uprobe_manager.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -493,15 +493,15 @@ class UProbeManager {
493493
*
494494
* @param binary The path to the binary on which to deploy Go HTTP2 probes.
495495
* @param elf_reader ELF reader for the binary.
496-
* @param dwarf_reader DWARF reader for the binary.
496+
* @param offset_locator DWARF reader for the binary.
497497
* @param pids The list of PIDs that are new instances of the binary. Used to populate symbol
498498
* addresses.
499499
* @return The number of uprobes deployed, or error. It is not considered an error if the binary
500500
* is not a Go binary or doesn't use a Go HTTP2 library; instead the return value will be
501501
* zero.
502502
*/
503503
StatusOr<int> AttachGoHTTP2UProbes(const std::string& binary, obj_tools::ElfReader* elf_reader,
504-
obj_tools::DwarfReader* dwarf_reader,
504+
GoOffsetLocator* offset_locator,
505505
const std::vector<int32_t>& pids);
506506

507507
/**
@@ -510,14 +510,14 @@ class UProbeManager {
510510
*
511511
* @param binary The path to the binary on which to deploy Go HTTP2 probes.
512512
* @param elf_reader ELF reader for the binary.
513-
* @param dwarf_reader DWARF reader for the binary.
513+
* @param offset_locator DWARF reader for the binary.
514514
* @param pids The list of PIDs that are new instances of the binary. Used to populate symbol
515515
* addresses.
516516
* @return The number of uprobes deployed, or error. It is not an error if the binary
517517
* is not a Go binary or doesn't use Go TLS; instead the return value will be zero.
518518
*/
519519
StatusOr<int> AttachGoTLSUProbes(const std::string& binary, obj_tools::ElfReader* elf_reader,
520-
obj_tools::DwarfReader* dwarf_reader,
520+
GoOffsetLocator* offset_locator,
521521
const std::vector<int32_t>& new_pids);
522522

523523
/**
@@ -579,14 +579,11 @@ class UProbeManager {
579579

580580
Status UpdateOpenSSLSymAddrs(px::stirling::obj_tools::RawFptrManager* fptrManager,
581581
std::filesystem::path container_lib, uint32_t pid);
582-
Status UpdateGoCommonSymAddrs(obj_tools::ElfReader* elf_reader,
583-
obj_tools::DwarfReader* dwarf_reader,
582+
Status UpdateGoCommonSymAddrs(obj_tools::ElfReader* elf_reader, GoOffsetLocator* offset_locator,
584583
const std::vector<int32_t>& pids);
585-
Status UpdateGoHTTP2SymAddrs(obj_tools::ElfReader* elf_reader,
586-
obj_tools::DwarfReader* dwarf_reader,
584+
Status UpdateGoHTTP2SymAddrs(obj_tools::ElfReader* elf_reader, GoOffsetLocator* offset_locator,
587585
const std::vector<int32_t>& pids);
588-
Status UpdateGoTLSSymAddrs(obj_tools::ElfReader* elf_reader, obj_tools::DwarfReader* dwarf_reader,
589-
const std::vector<int32_t>& pids);
586+
Status UpdateGoTLSSymAddrs(GoOffsetLocator* offset_locator, const std::vector<int32_t>& pids);
590587
Status UpdateNodeTLSWrapSymAddrs(int32_t pid, const std::filesystem::path& node_exe,
591588
const SemVer& ver);
592589

0 commit comments

Comments
 (0)