This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-rover.git
The following commit(s) were added to refs/heads/main by this push:
new 7c5e314 Support analyze the data from GoTLS (#46)
7c5e314 is described below
commit 7c5e3141444730bb2896a90639012c98d912624a
Author: mrproliu <[email protected]>
AuthorDate: Fri Aug 12 08:07:57 2022 +0800
Support analyze the data from GoTLS (#46)
---
CHANGES.md | 2 +-
bpf/profiling/network/args.h | 2 +
bpf/profiling/network/go_tls.c | 223 +++++++++++++++++++++++
bpf/profiling/network/go_tls.h | 150 ++++++++++++++++
bpf/profiling/network/netmonitor.c | 37 ++--
docs/en/setup/configuration/profiling.md | 2 +-
go.mod | 1 +
go.sum | 3 +
pkg/profiling/task/network/enums.go | 7 +
pkg/profiling/task/network/linker.go | 139 ++++++++++++---
pkg/profiling/task/network/ssl.go | 252 +++++++++++++++++++++++++-
pkg/tools/elf/abi.go | 146 +++++++++++++++
pkg/tools/elf/dwarf.go | 83 +++++++++
pkg/tools/elf/dwarf_init.go | 293 +++++++++++++++++++++++++++++++
pkg/tools/elf/elf.go | 107 +++++++++++
pkg/tools/elf/enums.go | 87 +++++++++
pkg/tools/elf/function.go | 39 ++++
pkg/tools/elf/structure.go | 37 ++++
pkg/tools/profiling/api.go | 1 +
pkg/tools/profiling/go_library.go | 2 +-
pkg/tools/profiling/kernel.go | 1 +
21 files changed, 1566 insertions(+), 48 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 5213e78..fab4e08 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,7 +7,7 @@ Release Notes.
#### Features
* Support `NETWORK` Profiling.
* Let the logger as a configurable module.
-* Support analyze the data of OpenSSL, BoringSSL library in `NETWORK`
Profiling.
+* Support analyze the data of OpenSSL, BoringSSL library, GoTLS in `NETWORK`
Profiling.
#### Bug Fixes
* Fixed reading process paths incorrect when running as a container.
diff --git a/bpf/profiling/network/args.h b/bpf/profiling/network/args.h
index b9bcc59..8247514 100644
--- a/bpf/profiling/network/args.h
+++ b/bpf/profiling/network/args.h
@@ -44,6 +44,8 @@
#define SOCKET_OPTS_TYPE_RESENT 17
#define SOCKET_OPTS_TYPE_SSL_WRITE 18
#define SOCKET_OPTS_TYPE_SSL_READ 19
+#define SOCKET_OPTS_TYPE_GOTLS_WRITE 20
+#define SOCKET_OPTS_TYPE_GOTLS_READ 21
// tracepoint enter
struct trace_event_raw_sys_enter {
diff --git a/bpf/profiling/network/go_tls.c b/bpf/profiling/network/go_tls.c
new file mode 100644
index 0000000..6422ad2
--- /dev/null
+++ b/bpf/profiling/network/go_tls.c
@@ -0,0 +1,223 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "go_tls.h"
+
+SEC("uprobe/casgstatus")
+int go_casgstatus(struct pt_regs* ctx) {
+ const void* sp = (const void*)PT_REGS_SP(ctx);
+ uint64_t* regs = go_regabi_regs(ctx);
+ if (regs == NULL) {
+ return 0;
+ }
+
+ __u64 id = bpf_get_current_pid_tgid();
+ __u32 tgid = id >> 32;
+ struct go_tls_args_symaddr_t* symaddrs = get_go_tls_args_symaddr(tgid);
+ if (symaddrs == NULL) {
+ return 0;
+ }
+
+ // get runtime.g
+ void* gptr = NULL;
+ assign_go_tls_arg(&gptr, sizeof(gptr), symaddrs->casg_status_gp_loc, sp,
regs);
+ if (gptr == NULL) {
+ return 0;
+ }
+
+ // get goid in runtime.g
+ int64_t goid;
+ bpf_probe_read(&goid, sizeof(goid), gptr + symaddrs->gid_offset);
+
+ // newval in runtime.g
+ __u32 status;
+ assign_go_tls_arg(&status, sizeof(status),
symaddrs->casg_status_new_val_loc, sp, regs);
+
+ const int runningStatus = 2;
+ if (status == runningStatus) {
+ set_goid(id, goid);
+ }
+ return 0;
+}
+
+SEC("uprobe/go_tls_write")
+int go_tls_write(struct pt_regs* ctx) {
+ __u64 id = bpf_get_current_pid_tgid();
+ __u32 tgid = id >> 32;
+
+ __u64 goid = get_goid(id);
+ if (goid == 0) {
+ return 0;
+ }
+
+ struct go_tls_args_symaddr_t* symaddrs = get_go_tls_args_symaddr(tgid);
+ if (symaddrs == NULL) {
+ return 0;
+ }
+
+ const void* sp = (const void*)PT_REGS_SP(ctx);
+ uint64_t* regs = go_regabi_regs(ctx);
+ if (regs == NULL) {
+ return 0;
+ }
+
+ struct go_tls_tgid_goid_t tgid_goid = {};
+ tgid_goid.tgid = tgid;
+ tgid_goid.goid = goid;
+
+ struct go_tls_connection_args_t data_args = {};
+ assign_go_tls_arg(&data_args.connection_ptr,
sizeof(data_args.connection_ptr), symaddrs->write_connection_loc, sp, regs);
+ assign_go_tls_arg(&data_args.buffer_ptr, sizeof(data_args.buffer_ptr),
symaddrs->write_buffer_loc, sp, regs);
+ bpf_map_update_elem(&go_tls_active_connection_args, &tgid_goid,
&data_args, 0);
+ return 0;
+}
+
+SEC("uretprobe/go_tls_write")
+int go_tls_write_ret(struct pt_regs* ctx) {
+ __u64 id = bpf_get_current_pid_tgid();
+ __u32 tgid = id >> 32;
+
+ __u64 goid = get_goid(id);
+ if (goid == 0) {
+ return 0;
+ }
+
+ struct go_tls_args_symaddr_t* symaddrs = get_go_tls_args_symaddr(tgid);
+ if (symaddrs == NULL) {
+ return 0;
+ }
+
+ const void* sp = (const void*)PT_REGS_SP(ctx);
+ uint64_t* regs = go_regabi_regs(ctx);
+ if (regs == NULL) {
+ return 0;
+ }
+
+ int64_t retval0 = 0;
+ assign_go_tls_arg(&retval0, sizeof(retval0), symaddrs->write_ret0_loc, sp,
regs);
+ struct go_interface retval1 = {};
+ assign_go_tls_arg(&retval1, sizeof(retval1), symaddrs->write_ret1_loc, sp,
regs);
+ if (retval1.ptr != 0) {
+ return 0;
+ }
+
+ struct go_tls_tgid_goid_t tgid_goid = {};
+ tgid_goid.tgid = tgid;
+ tgid_goid.goid = goid;
+
+ struct go_tls_connection_args_t *args =
bpf_map_lookup_elem(&go_tls_active_connection_args, &tgid_goid);
+ if (args) {
+ struct go_interface conn_intf = {};
+ conn_intf.type = 1;
+ conn_intf.ptr = args->connection_ptr;
+ int fd = get_fd_from_go_tls_conn(conn_intf, symaddrs);
+
+ set_conn_as_ssl(ctx, tgid, fd, SOCKET_OPTS_TYPE_GOTLS_WRITE);
+
+ struct sock_data_args_t data_args = {};
+ data_args.fd = fd;
+ data_args.buf = args->buffer_ptr;
+
+ process_write_data(ctx, id, &data_args, retval0,
SOCK_DATA_DIRECTION_EGRESS, false, SOCKET_OPTS_TYPE_GOTLS_WRITE, true);
+ }
+ bpf_map_delete_elem(&go_tls_active_connection_args, &tgid_goid);
+ return 0;
+}
+
+SEC("uprobe/go_tls_read")
+int go_tls_read(struct pt_regs* ctx) {
+ __u64 id = bpf_get_current_pid_tgid();
+ __u32 tgid = id >> 32;
+
+ __u64 goid = get_goid(id);
+ if (goid == 0) {
+ return 0;
+ }
+
+ struct go_tls_args_symaddr_t* symaddrs = get_go_tls_args_symaddr(tgid);
+ if (symaddrs == NULL) {
+ return 0;
+ }
+
+ const void* sp = (const void*)PT_REGS_SP(ctx);
+ uint64_t* regs = go_regabi_regs(ctx);
+ if (regs == NULL) {
+ return 0;
+ }
+
+ struct go_tls_tgid_goid_t tgid_goid = {};
+ tgid_goid.tgid = tgid;
+ tgid_goid.goid = goid;
+
+ struct go_tls_connection_args_t data_args = {};
+ assign_go_tls_arg(&data_args.connection_ptr,
sizeof(data_args.connection_ptr), symaddrs->read_connection_loc, sp, regs);
+ assign_go_tls_arg(&data_args.buffer_ptr, sizeof(data_args.buffer_ptr),
symaddrs->read_buffer_loc, sp, regs);
+ bpf_map_update_elem(&go_tls_active_connection_args, &tgid_goid,
&data_args, 0);
+ return 0;
+}
+
+SEC("uretprobe/go_tls_read")
+int go_tls_read_ret(struct pt_regs* ctx) {
+ __u64 id = bpf_get_current_pid_tgid();
+ __u32 tgid = id >> 32;
+
+ __u64 goid = get_goid(id);
+ if (goid == 0) {
+ return 0;
+ }
+
+ struct go_tls_args_symaddr_t* symaddrs = get_go_tls_args_symaddr(tgid);
+ if (symaddrs == NULL) {
+ return 0;
+ }
+
+ const void* sp = (const void*)PT_REGS_SP(ctx);
+ uint64_t* regs = go_regabi_regs(ctx);
+ if (regs == NULL) {
+ return 0;
+ }
+
+ int64_t retval0 = 0;
+ assign_go_tls_arg(&retval0, sizeof(retval0), symaddrs->read_ret0_loc, sp,
regs);
+ struct go_interface retval1 = {};
+ assign_go_tls_arg(&retval1, sizeof(retval1), symaddrs->read_ret1_loc, sp,
regs);
+ if (retval1.ptr != 0) {
+ return 0;
+ }
+
+ struct go_tls_tgid_goid_t tgid_goid = {};
+ tgid_goid.tgid = tgid;
+ tgid_goid.goid = goid;
+
+ struct go_tls_connection_args_t *args =
bpf_map_lookup_elem(&go_tls_active_connection_args, &tgid_goid);
+ if (args) {
+ struct go_interface conn_intf = {};
+ conn_intf.type = 1;
+ conn_intf.ptr = args->connection_ptr;
+ int fd = get_fd_from_go_tls_conn(conn_intf, symaddrs);
+
+ set_conn_as_ssl(ctx, tgid, fd, SOCKET_OPTS_TYPE_GOTLS_READ);
+
+ struct sock_data_args_t data_args = {};
+ data_args.fd = fd;
+ data_args.buf = args->buffer_ptr;
+
+ process_write_data(ctx, id, &data_args, retval0,
SOCK_DATA_DIRECTION_INGRESS, false, SOCKET_OPTS_TYPE_GOTLS_WRITE, true);
+ }
+ bpf_map_delete_elem(&go_tls_active_connection_args, &tgid_goid);
+ return 0;
+}
\ No newline at end of file
diff --git a/bpf/profiling/network/go_tls.h b/bpf/profiling/network/go_tls.h
new file mode 100644
index 0000000..7b800eb
--- /dev/null
+++ b/bpf/profiling/network/go_tls.h
@@ -0,0 +1,150 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+struct go_tls_arg_location_t {
+ __u32 type;
+ __u32 offset;
+};
+
+struct go_tls_args_symaddr_t {
+ __u64 fd_sys_offset;
+ __u64 tls_conn_offset;
+ __u64 gid_offset;
+ __u64 tcp_conn_offset;
+
+ // casg
+ struct go_tls_arg_location_t casg_status_gp_loc;
+ struct go_tls_arg_location_t casg_status_new_val_loc;
+
+ // read
+ struct go_tls_arg_location_t read_connection_loc;
+ struct go_tls_arg_location_t read_buffer_loc;
+ struct go_tls_arg_location_t read_ret0_loc;
+ struct go_tls_arg_location_t read_ret1_loc;
+
+ // write
+ struct go_tls_arg_location_t write_connection_loc;
+ struct go_tls_arg_location_t write_buffer_loc;
+ struct go_tls_arg_location_t write_ret0_loc;
+ struct go_tls_arg_location_t write_ret1_loc;
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 10000);
+ __type(key, __u32);
+ __type(value, struct go_tls_args_symaddr_t);
+} go_tls_args_symaddr_map SEC(".maps");
+static __inline struct go_tls_args_symaddr_t* get_go_tls_args_symaddr(__u32
tgid) {
+ struct go_tls_args_symaddr_t *addr =
bpf_map_lookup_elem(&go_tls_args_symaddr_map, &tgid);
+ return addr;
+}
+
+struct go_tls_tgid_goid_t {
+ __u64 tgid;
+ __u64 goid;
+};
+struct go_tls_connection_args_t {
+ void* connection_ptr;
+ char* buffer_ptr;
+};
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 10000);
+ __type(key, struct go_tls_tgid_goid_t);
+ __type(value, struct go_tls_connection_args_t);
+} go_tls_active_connection_args SEC(".maps");
+
+
+struct go_regabi_regs_t {
+ __u64 regs[9];
+};
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __type(key, __u32);
+ __type(value, struct go_regabi_regs_t);
+ __uint(max_entries, 1);
+} go_regabi_regs_map SEC(".maps");
+// Copies the registers of the golang ABI, so that they can be
+// easily accessed using an offset.
+static __inline uint64_t* go_regabi_regs(const struct pt_regs* ctx) {
+ __u32 zero = 0;
+ struct go_regabi_regs_t* regs_heap_var =
bpf_map_lookup_elem(&go_regabi_regs_map, &zero);
+ if (regs_heap_var == NULL) {
+ return NULL;
+ }
+
+ regs_heap_var->regs[0] = ctx->rax;
+ regs_heap_var->regs[1] = ctx->rbx;
+ regs_heap_var->regs[2] = ctx->rcx;
+ regs_heap_var->regs[3] = ctx->rdi;
+ regs_heap_var->regs[4] = ctx->rsi;
+ regs_heap_var->regs[5] = ctx->r8;
+ regs_heap_var->regs[6] = ctx->r9;
+ regs_heap_var->regs[7] = ctx->r10;
+ regs_heap_var->regs[8] = ctx->r11;
+
+ return regs_heap_var->regs;
+}
+
+struct go_interface {
+ int64_t type;
+ void* ptr;
+};
+
+static __inline void assign_go_tls_arg(void* arg, size_t arg_size, struct
go_tls_arg_location_t loc, const void* sp,
+ uint64_t* regs) {
+ // stack type
+ if (loc.type == 1) {
+ bpf_probe_read(arg, arg_size, sp + loc.offset);
+ } else if (loc.type == 2) {
+ // register type
+ if (loc.offset >= 0 && loc.offset <= 30) {
+ bpf_probe_read(arg, arg_size, (char*)regs + loc.offset);
+ }
+ }
+}
+
+static __inline int get_fd_from_go_tls_conn(struct go_interface conn, struct
go_tls_args_symaddr_t* symaddr) {
+ // read connection
+ bpf_probe_read(&conn, sizeof(conn), conn.ptr + symaddr->tls_conn_offset);
+
+ if (conn.type != symaddr->tcp_conn_offset) {
+ return 0;
+ }
+
+ void* fd_ptr;
+ bpf_probe_read(&fd_ptr, sizeof(fd_ptr), conn.ptr);
+ __u64 sysfd;
+ bpf_probe_read(&sysfd, sizeof(sysfd), fd_ptr + symaddr->fd_sys_offset);
+ return sysfd;
+}
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 1024);
+ __type(key, __u64);
+ __type(value, __u64);
+} go_tgid_goid_map SEC(".maps");
+static __inline __u64 get_goid(__u64 tgid) {
+ __u64 *val;
+ val = bpf_map_lookup_elem(&go_tgid_goid_map, &tgid);
+ return !val ? 0 : *val;
+}
+static __inline void set_goid(__u64 tgid, __u64 goid) {
+ bpf_map_update_elem(&go_tgid_goid_map, &tgid, &goid, 0);
+}
\ No newline at end of file
diff --git a/bpf/profiling/network/netmonitor.c
b/bpf/profiling/network/netmonitor.c
index c929b22..233a51b 100644
--- a/bpf/profiling/network/netmonitor.c
+++ b/bpf/profiling/network/netmonitor.c
@@ -359,22 +359,24 @@ static __always_inline void process_write_data(struct
pt_regs *ctx, __u64 id, st
}
}
- // add statics
- __u64 exe_time = curr_nacs - args->start_nacs;
- if (data_direction == SOCK_DATA_DIRECTION_EGRESS) {
- conn->write_bytes += bytes_count;
- conn->write_count++;
- conn->write_exe_time += exe_time;
-
- add_to_socket_connection_stats_histogram(conid, conn->random_id,
SOCK_DATA_DIRECTION_EGRESS,
- SOCKET_CONNECTION_STATS_HISTOGRAM_DATA_TYPE_EXE_TIME,
exe_time);
- } else {
- conn->read_bytes += bytes_count;
- conn->read_count++;
- conn->read_exe_time += exe_time;
-
- add_to_socket_connection_stats_histogram(conid, conn->random_id,
SOCK_DATA_DIRECTION_INGRESS,
-
SOCKET_CONNECTION_STATS_HISTOGRAM_DATA_TYPE_EXE_TIME, exe_time);
+ // add statics when is not ssl(native buffer)
+ if (ssl == false) {
+ __u64 exe_time = curr_nacs - args->start_nacs;
+ if (data_direction == SOCK_DATA_DIRECTION_EGRESS) {
+ conn->write_bytes += bytes_count;
+ conn->write_count++;
+ conn->write_exe_time += exe_time;
+
+ add_to_socket_connection_stats_histogram(conid, conn->random_id,
SOCK_DATA_DIRECTION_EGRESS,
+
SOCKET_CONNECTION_STATS_HISTOGRAM_DATA_TYPE_EXE_TIME, exe_time);
+ } else {
+ conn->read_bytes += bytes_count;
+ conn->read_count++;
+ conn->read_exe_time += exe_time;
+
+ add_to_socket_connection_stats_histogram(conid, conn->random_id,
SOCK_DATA_DIRECTION_INGRESS,
+
SOCKET_CONNECTION_STATS_HISTOGRAM_DATA_TYPE_EXE_TIME, exe_time);
+ }
}
// RTT
@@ -1127,4 +1129,5 @@ int tcp_drop(struct pt_regs *ctx) {
return 0;
}
-#include "openssl.c"
\ No newline at end of file
+#include "openssl.c"
+#include "go_tls.c"
\ No newline at end of file
diff --git a/docs/en/setup/configuration/profiling.md
b/docs/en/setup/configuration/profiling.md
index 0e3ba42..94d9674 100644
--- a/docs/en/setup/configuration/profiling.md
+++ b/docs/en/setup/configuration/profiling.md
@@ -30,7 +30,7 @@ Off CPU Profiling task is attach the `finish_task_switch` in
`krobe` to profilin
### Network
Network Profiling task is intercept IO-related syscall and `urprobe` in
process to identify the network traffic and generate the metrics.
-Also, the following protocol are supported for analyzing using OpenSSL
library, BoringSSL library or plaintext:
+Also, the following protocol are supported for analyzing using OpenSSL
library, BoringSSL library, GoTLS or plaintext:
1. HTTP
2. MySQL
diff --git a/go.mod b/go.mod
index 5c74485..6e4a631 100644
--- a/go.mod
+++ b/go.mod
@@ -13,6 +13,7 @@ require (
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.3.0
github.com/spf13/viper v1.10.1
+ golang.org/x/arch v0.0.0-20220722155209-00200b7164a7
golang.org/x/sys v0.0.0-20211210111614-af8b64212486
google.golang.org/grpc v1.44.0
k8s.io/api v0.23.5
diff --git a/go.sum b/go.sum
index 0d475d3..d2876d2 100644
--- a/go.sum
+++ b/go.sum
@@ -452,6 +452,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod
h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
go.uber.org/atomic v1.7.0/go.mod
h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod
h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
+golang.org/x/arch v0.0.0-20220722155209-00200b7164a7
h1:VBQqJMNMRfQsWSiCTLgz9XjAfWlgnJAPv8nsp1HF8Tw=
+golang.org/x/arch v0.0.0-20220722155209-00200b7164a7/go.mod
h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod
h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod
h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -933,6 +935,7 @@ k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod
h1:jPW/WVKK9YHAvNhRxK0md/
k8s.io/utils v0.0.0-20211116205334-6203023598ed
h1:ck1fRPWPJWsMd8ZRFsWc6mh/zHp5fZ/shhbrgPUxDAE=
k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod
h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod
h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6
h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=
diff --git a/pkg/profiling/task/network/enums.go
b/pkg/profiling/task/network/enums.go
index 2f5981f..5cbbe5f 100644
--- a/pkg/profiling/task/network/enums.go
+++ b/pkg/profiling/task/network/enums.go
@@ -124,3 +124,10 @@ func (c ConnectionProtocol) String() string {
return unknown
}
}
+
+type GoTLSArgsLocationType uint32
+
+const (
+ GoTLSArgsLocationTypeStack GoTLSArgsLocationType = 1
+ GoTLSArgsLocationTypeRegister GoTLSArgsLocationType = 2
+)
diff --git a/pkg/profiling/task/network/linker.go
b/pkg/profiling/task/network/linker.go
index c2d8396..36995d2 100644
--- a/pkg/profiling/task/network/linker.go
+++ b/pkg/profiling/task/network/linker.go
@@ -26,7 +26,10 @@ import (
"os"
"sync"
+ "golang.org/x/arch/x86/x86asm"
+
"github.com/apache/skywalking-rover/pkg/tools"
+ "github.com/apache/skywalking-rover/pkg/tools/elf"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
@@ -88,7 +91,7 @@ func NewLinker() *Linker {
type UProbeExeFile struct {
addr string
found bool
- liker *Linker
+ linker *Linker
realFile *link.Executable
}
@@ -183,50 +186,138 @@ func (m *Linker) OpenUProbeExeFile(path string)
*UProbeExeFile {
return &UProbeExeFile{
found: true,
addr: path,
- liker: m,
+ linker: m,
realFile: executable,
}
}
-func (m *UProbeExeFile) AddLink(symbol string, enter, exit *ebpf.Program) {
- m.AddLinkWithType(symbol, true, enter)
- m.AddLinkWithType(symbol, false, exit)
+func (u *UProbeExeFile) AddLink(symbol string, enter, exit *ebpf.Program) {
+ u.AddLinkWithType(symbol, true, enter)
+ u.AddLinkWithType(symbol, false, exit)
+}
+
+func (u *UProbeExeFile) AddGoLink(symbol string, enter, exit *ebpf.Program,
elfFile *elf.File) {
+ u.AddGoLinkWithType(symbol, true, enter, elfFile)
+ u.AddGoLinkWithType(symbol, false, exit, elfFile)
}
-func (m *UProbeExeFile) AddLinkWithType(symbol string, enter bool, p
*ebpf.Program) {
- if !m.found {
+func (u *UProbeExeFile) AddLinkWithType(symbol string, enter bool, p
*ebpf.Program) {
+ if !u.found {
return
}
- var t string
- if enter {
- t = "enter"
- } else {
- t = "exit"
+ lk, err := u.addLinkWithType0(symbol, enter, p, 0)
+ if err != nil {
+ u.linker.errors = multierror.Append(u.linker.errors,
fmt.Errorf("file: %s, symbol: %s, type: %s, error: %v",
+ u.addr, symbol, u.parseEnterOrExitString(enter), err))
+ } else if lk != nil {
+ log.Debugf("attach to the uprobe, file: %s, symbol: %s, type:
%s", u.addr, symbol, u.parseEnterOrExitString(enter))
+ u.linker.closers = append(u.linker.closers, lk)
}
+}
+func (u *UProbeExeFile) addLinkWithType0(symbol string, enter bool, p
*ebpf.Program, customizeOffset uint64) (link.Link, error) {
// check already linked
- uprobeIdentity := fmt.Sprintf("%s_%s_%t", m.addr, symbol, enter)
- if m.liker.linkedUProbes[uprobeIdentity] {
- log.Debugf("the uprobe already attached, so ignored. file: %s,
symbol: %s, type: %s", m.addr, symbol, t)
- return
+ uprobeIdentity := fmt.Sprintf("%s_%s_%t_%d", u.addr, symbol, enter,
customizeOffset)
+ if u.linker.linkedUProbes[uprobeIdentity] {
+ log.Debugf("the uprobe already attached, so ignored. file: %s,
symbol: %s, type: %s", u.addr, symbol,
+ u.parseEnterOrExitString(enter))
+ return nil, nil
}
- m.liker.linkedUProbes[uprobeIdentity] = true
+ u.linker.linkedUProbes[uprobeIdentity] = true
var fun func(symbol string, prog *ebpf.Program, opts
*link.UprobeOptions) (link.Link, error)
if enter {
- fun = m.realFile.Uprobe
+ fun = u.realFile.Uprobe
} else {
- fun = m.realFile.Uretprobe
+ fun = u.realFile.Uretprobe
+ }
+
+ var opts *link.UprobeOptions
+ if customizeOffset > 0 {
+ opts = &link.UprobeOptions{
+ Offset: customizeOffset,
+ }
+ }
+ return fun(symbol, p, opts)
+}
+
+func (u *UProbeExeFile) AddGoLinkWithType(symbol string, enter bool, p
*ebpf.Program, elfFile *elf.File) {
+ // if is entered type of probe, then same with the other programs
+ if enter {
+ u.AddLinkWithType(symbol, enter, p)
+ return
}
- lk, err := fun(symbol, p, nil)
+ links, err := u.addGoExitLink0(symbol, p, elfFile)
if err != nil {
- m.liker.errors = multierror.Append(m.liker.errors,
fmt.Errorf("file: %s, symbol: %s, type: %s, error: %v",
- m.addr, symbol, t, err))
+ u.linker.errors = multierror.Append(u.linker.errors,
fmt.Errorf("file: %s, symbol: %s, type: %s, error: %v",
+ u.addr, symbol, u.parseEnterOrExitString(enter), err))
} else {
- log.Debugf("attach to the uprobe, file: %s, symbol: %s, type:
%s", m.addr, symbol, t)
- m.liker.closers = append(m.liker.closers, lk)
+ log.Debugf("attach to the go uprobe, file: %s, symbol: %s,
type: %s", u.addr, symbol, u.parseEnterOrExitString(enter))
+ for _, l := range links {
+ u.linker.closers = append(u.linker.closers, l)
+ }
+ }
+}
+
+func (u *UProbeExeFile) addGoExitLink0(symbol string, p *ebpf.Program, elfFile
*elf.File) ([]link.Link, error) {
+ // find the symbol
+ targetSymbol := elfFile.FindSymbol(symbol)
+ if targetSymbol == nil {
+ return nil, fmt.Errorf("could not found the symbol")
+ }
+
+ // find the symbol real data buffer
+ buffer, err := elfFile.ReadSymbolData(".text", targetSymbol.Location,
targetSymbol.Size)
+ if err != nil {
+ return nil, fmt.Errorf("reading symbol data error: %v", err)
+ }
+
+ // find the base offset
+ targetBaseOffset :=
elfFile.FindBaseOffsetForAttach(targetSymbol.Location)
+ if targetBaseOffset == 0 {
+ return nil, fmt.Errorf("could not found the symbol base
address")
+ }
+
+ // based on the base offset and symbol data buffer
+ // calculate all RET addresses
+ // https://github.com/iovisor/bcc/issues/1320#issuecomment-407927542
+ var offsets []uint64
+ for i := 0; i < int(targetSymbol.Size); {
+ inst, err := x86asm.Decode(buffer[i:], 64)
+ if err != nil {
+ return nil, fmt.Errorf("error decode the function data:
%v", err)
+ }
+
+ if inst.Op == x86asm.RET {
+ offsets = append(offsets, targetBaseOffset+uint64(i))
+ }
+
+ i += inst.Len
+ }
+
+ if len(offsets) == 0 {
+ return nil, fmt.Errorf("could not found any return offsets")
+ }
+ log.Debugf("found reuturn offsets of the symbol, symbol: %s, size: %d",
symbol, len(offsets))
+
+ var result []link.Link
+ for _, offset := range offsets {
+ l, err := u.addLinkWithType0(symbol, true, p, offset)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, l)
+ log.Debugf("attach to the return probe of the go program,
symbol: %s, offset: %d", symbol, offset)
+ }
+ return result, nil
+}
+
+func (u *UProbeExeFile) parseEnterOrExitString(enter bool) string {
+ if enter {
+ return "enter"
}
+ return "exit"
}
func (m *Linker) HasError() error {
diff --git a/pkg/profiling/task/network/ssl.go
b/pkg/profiling/task/network/ssl.go
index ab69f96..0f5944e 100644
--- a/pkg/profiling/task/network/ssl.go
+++ b/pkg/profiling/task/network/ssl.go
@@ -18,20 +18,31 @@
package network
import (
+ "bytes"
+ "encoding/binary"
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
- "github.com/apache/skywalking-rover/pkg/tools/profiling"
-
"github.com/apache/skywalking-rover/pkg/tools"
-
+ "github.com/apache/skywalking-rover/pkg/tools/elf"
+ "github.com/apache/skywalking-rover/pkg/tools/host"
"github.com/apache/skywalking-rover/pkg/tools/path"
+ "github.com/apache/skywalking-rover/pkg/tools/profiling"
)
-var openSSLVersionRegex =
regexp.MustCompile(`^OpenSSL\s+(?P<Major>\d)\.(?P<Minor>\d)\.(?P<Fix>\d+)\w+`)
+var (
+ openSSLVersionRegex =
regexp.MustCompile(`^OpenSSL\s+(?P<Major>\d)\.(?P<Minor>\d)\.(?P<Fix>\d+)\w+`)
+ goVersionRegex =
regexp.MustCompile(`^go(?P<Major>\d)\.(?P<Minor>\d+)`)
+ goTLSWriteSymbol = "crypto/tls.(*Conn).Write"
+ goTLSReadSymbol = "crypto/tls.(*Conn).Read"
+ goTLSGIDStatusSymbol = "runtime.casgstatus"
+ goTLSPollFDSymbol = "internal/poll.FD"
+ goTLSConnSymbol = "crypto/tls.Conn"
+ goTLSRuntimeG = "runtime.g"
+)
type OpenSSLFdSymAddrConfigInBPF struct {
BIOReadOffset uint32
@@ -55,6 +66,11 @@ func addSSLProcess(pid int, bpf *bpfObjects, linker *Linker)
error {
return err1
}
+ // GoTLS
+ if err1 := processGoProcess(pid, bpf, linker, modules); err1 != nil {
+ return err1
+ }
+
return nil
}
@@ -128,6 +144,219 @@ func processEnvoyProcess(_ int, bpf *bpfObjects, linker
*Linker, modules []*prof
return linker.HasError()
}
+type SymbolLocation struct {
+ Type GoTLSArgsLocationType
+ Offset uint32
+}
+
+type GoTLSSymbolAddresses struct {
+ // net.Conn addresses
+ FDSysFDOffset uint64
+ TLSConnOffset uint64
+ GIDOffset uint64
+ TCPConnOffset uint64
+
+ // casgstatus(goroutine status change) function relate locations
+ CasgStatusGPLoc SymbolLocation
+ CasgStatusNEWValLoc SymbolLocation
+
+ // write function relate locations
+ WriteConnectionLoc SymbolLocation
+ WriteBufferLoc SymbolLocation
+ WriteRet0Loc SymbolLocation
+ WriteRet1Loc SymbolLocation
+
+ // write function relate locations
+ ReadConnectionLoc SymbolLocation
+ ReadBufferLoc SymbolLocation
+ ReadRet0Loc SymbolLocation
+ ReadRet1Loc SymbolLocation
+}
+
+type GoStringInC struct {
+ Ptr uint64
+ Size uint64
+}
+
+func processGoProcess(pid int, bpf *bpfObjects, linker *Linker, modules
[]*profiling.Module) error {
+ // check current process is go program
+ buildVersionSymbol := searchSymbol(modules, func(a, b string) bool {
+ return a == b
+ }, "runtime.buildVersion")
+ if buildVersionSymbol == nil {
+ log.Debugf("current process is not Go program, so won't add the
GoTLS protos. pid: %d", pid)
+ return nil
+ }
+ pidExeFile := host.GetFileInHost(fmt.Sprintf("/proc/%d/exe", pid))
+ elfFile, err := elf.NewFile(pidExeFile)
+ if err != nil {
+ return fmt.Errorf("read executable file error: %v", err)
+ }
+ defer elfFile.Close()
+
+ _, minor, err := getGoVersion(elfFile, buildVersionSymbol)
+ if err != nil {
+ return err
+ }
+
+ // generate symbol offsets
+ symbolConfig, elfFile, err := generateGOTLSSymbolOffsets(modules, pid,
elfFile, minor)
+ if err != nil {
+ return err
+ }
+ if symbolConfig == nil || elfFile == nil {
+ return nil
+ }
+
+ // setting the locations
+ if err := bpf.GoTlsArgsSymaddrMap.Put(uint32(pid), symbolConfig); err
!= nil {
+ return fmt.Errorf("setting the Go TLS argument location
failure, pid: %d, error: %v", pid, err)
+ }
+
+ // uprobes
+ exeFile := linker.OpenUProbeExeFile(pidExeFile)
+ exeFile.AddLinkWithType("runtime.casgstatus", true, bpf.GoCasgstatus)
+ exeFile.AddGoLink(goTLSWriteSymbol, bpf.GoTlsWrite, bpf.GoTlsWriteRet,
elfFile)
+ exeFile.AddGoLink(goTLSReadSymbol, bpf.GoTlsRead, bpf.GoTlsReadRet,
elfFile)
+
+ return linker.HasError()
+}
+
+func getGoVersion(elfFile *elf.File, versionSymbol *profiling.Symbol) (major,
minor int, err error) {
+ buffer, err := elfFile.ReadSymbolData(".data", versionSymbol.Location,
versionSymbol.Size)
+ if err != nil {
+ return 0, 0, fmt.Errorf("reading go version struct info
failure: %v", err)
+ }
+ var t = GoStringInC{}
+ buf := bytes.NewReader(buffer)
+ err = binary.Read(buf, binary.LittleEndian, &t)
+ if err != nil {
+ return 0, 0, fmt.Errorf("read the go structure failure: %v",
err)
+ }
+ buffer, err = elfFile.ReadSymbolData(".data", t.Ptr, t.Size)
+ if err != nil {
+ return 0, 0, fmt.Errorf("read the go version failure: %v", err)
+ }
+
+ // parse versions
+ submatch := goVersionRegex.FindStringSubmatch(string(buffer))
+ if len(submatch) != 3 {
+ return 0, 0, fmt.Errorf("the go version is failure to identify,
version: %s", string(buffer))
+ }
+ major, err = strconv.Atoi(submatch[1])
+ if err != nil {
+ return 0, 0, fmt.Errorf("the marjor version is a number,
version: %s", string(buffer))
+ }
+ minor, err = strconv.Atoi(submatch[2])
+ if err != nil {
+ return 0, 0, fmt.Errorf("the minor version is a number,
version: %s", string(buffer))
+ }
+
+ return major, minor, nil
+}
+
+func generateGOTLSSymbolOffsets(modules []*profiling.Module, _ int, elfFile
*elf.File, minorVersion int) (*GoTLSSymbolAddresses, *elf.File, error) {
+ reader, err := elfFile.NewDwarfReader(
+ goTLSReadSymbol, goTLSWriteSymbol, goTLSGIDStatusSymbol,
+ goTLSPollFDSymbol, goTLSConnSymbol, goTLSRuntimeG)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ symbolAddresses := &GoTLSSymbolAddresses{}
+
+ sym := searchSymbol(modules, func(a, b string) bool {
+ return a == b
+ }, "go.itab.*net.TCPConn,net.Conn")
+ if sym == nil {
+ return nil, nil, fmt.Errorf("could found the tcp connection
symbol")
+ }
+ symbolAddresses.TCPConnOffset = sym.Location
+
+ readFunction := reader.GetFunction(goTLSReadSymbol)
+ if readFunction == nil {
+ log.Warnf("could not found the go tls read symbol: %s",
goTLSReadSymbol)
+ return nil, nil, nil
+ }
+ writeFunction := reader.GetFunction(goTLSWriteSymbol)
+ if writeFunction == nil {
+ log.Warnf("could not found the go tls write symbol: %s",
goTLSWriteSymbol)
+ return nil, nil, nil
+ }
+ gidStatusFunction := reader.GetFunction(goTLSGIDStatusSymbol)
+ if gidStatusFunction == nil {
+ log.Warnf("could not found the goid status change symbol: %s",
goTLSGIDStatusSymbol)
+ return nil, nil, nil
+ }
+
+ var retValArg0, retValArg1 = "~r1", "~r2"
+ if minorVersion >= 18 {
+ retValArg0, retValArg1 = "~r0", "~r1"
+ }
+
+ // build the symbols
+ var assignError error
+ // offset
+ assignError = assignGoTLSStructureOffset(assignError, reader,
goTLSPollFDSymbol, "Sysfd", &symbolAddresses.FDSysFDOffset)
+ assignError = assignGoTLSStructureOffset(assignError, reader,
goTLSConnSymbol, "conn", &symbolAddresses.TLSConnOffset)
+ assignError = assignGoTLSStructureOffset(assignError, reader,
goTLSRuntimeG, "goid", &symbolAddresses.GIDOffset)
+
+ // gid status change
+ assignError = assignGoTLSArgsLocation(assignError, gidStatusFunction,
"gp", &symbolAddresses.CasgStatusGPLoc)
+ assignError = assignGoTLSArgsLocation(assignError, gidStatusFunction,
"newval", &symbolAddresses.CasgStatusNEWValLoc)
+
+ // write
+ assignError = assignGoTLSArgsLocation(assignError, writeFunction, "c",
&symbolAddresses.WriteConnectionLoc)
+ assignError = assignGoTLSArgsLocation(assignError, writeFunction, "b",
&symbolAddresses.WriteBufferLoc)
+ assignError = assignGoTLSArgsLocation(assignError, writeFunction,
retValArg0, &symbolAddresses.WriteRet0Loc)
+ assignError = assignGoTLSArgsLocation(assignError, writeFunction,
retValArg1, &symbolAddresses.WriteRet1Loc)
+ // read
+ assignError = assignGoTLSArgsLocation(assignError, readFunction, "c",
&symbolAddresses.ReadConnectionLoc)
+ assignError = assignGoTLSArgsLocation(assignError, readFunction, "b",
&symbolAddresses.ReadBufferLoc)
+ assignError = assignGoTLSArgsLocation(assignError, readFunction,
retValArg0, &symbolAddresses.ReadRet0Loc)
+ assignError = assignGoTLSArgsLocation(assignError, readFunction,
retValArg1, &symbolAddresses.ReadRet1Loc)
+
+ return symbolAddresses, elfFile, assignError
+}
+
+func assignGoTLSStructureOffset(err error, reader *elf.DwarfReader,
structName, fieldName string, dest *uint64) error {
+ if err != nil {
+ return err
+ }
+ structure := reader.GetStructure(structName)
+ if structure == nil {
+ return fmt.Errorf("the structure is not found, name: %s",
structName)
+ }
+ field := structure.GetField(fieldName)
+ if field == nil {
+ return fmt.Errorf("the field is not found in structure,
structure name: %s, field name: %s", structName, fieldName)
+ }
+ *dest = uint64(field.Offset)
+ return nil
+}
+
+func assignGoTLSArgsLocation(err error, function *elf.FunctionInfo, argName
string, dest *SymbolLocation) error {
+ if err != nil {
+ return err
+ }
+ var kSPOffset uint32 = 8
+ args := function.Args(argName)
+ if args == nil {
+ return fmt.Errorf("the args is not found, function: %s, args
name: %s", function.Name(), argName)
+ }
+ if args.Location.Type == elf.ArgLocationTypeStack {
+ dest.Type = GoTLSArgsLocationTypeStack
+ dest.Offset = uint32(args.Location.Offset) + kSPOffset
+ } else if args.Location.Type == elf.ArgLocationTypeRegister {
+ dest.Type = GoTLSArgsLocationTypeRegister
+ dest.Offset = uint32(args.Location.Offset)
+ } else {
+ return fmt.Errorf("the location type is not support, function:
%s, args name: %s, type: %d",
+ function.Name(), argName, args.Location.Type)
+ }
+ return nil
+}
+
func findProcessModules(modules []*profiling.Module, moduleNames ...string)
(map[string]*profiling.Module, error) {
result := make(map[string]*profiling.Module)
for _, mod := range modules {
@@ -191,3 +420,18 @@ func buildSSLSymAddrConfig(libcryptoPath string)
(*OpenSSLFdSymAddrConfigInBPF,
}
return nil, fmt.Errorf("could not fount the version of the
libcrypto.so")
}
+
+type stringVerify func(a, b string) bool
+
+func searchSymbol(modules []*profiling.Module, verify stringVerify, values
...string) *profiling.Symbol {
+ for _, mod := range modules {
+ for _, s := range mod.Symbols {
+ for _, validator := range values {
+ if verify(s.Name, validator) {
+ return s
+ }
+ }
+ }
+ }
+ return nil
+}
diff --git a/pkg/tools/elf/abi.go b/pkg/tools/elf/abi.go
new file mode 100644
index 0000000..de34660
--- /dev/null
+++ b/pkg/tools/elf/abi.go
@@ -0,0 +1,146 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package elf
+
+import (
+ "fmt"
+ "strings"
+)
+
+type ArgLocation struct {
+ Type LocationType
+ Offset uint64
+}
+
+type ArgumentLocator interface {
+ // GetLocation of the argument
+ GetLocation(typeClass TypeClass, typeSize uint64, alignmentSize uint64,
primitivesCount int, isRetArg bool) (*ArgLocation, error)
+}
+
+func NewArgumentLocator(r *DwarfReader) ArgumentLocator {
+ if r.language == ReaderLanguageGolang {
+ // is register-based ABI
+ // example: "Go cmd/comile go1.18; regabi"
+ if strings.Contains(r.producer, "regabi") {
+ return newGolangRegisterLocator()
+ }
+ return &GolangStackLocator{}
+ }
+ return &UnknownLocator{language: r.language}
+}
+
+type GolangRegisterLocator struct {
+ curStackOffset uint64
+ curIntArgOffset uint64
+ curFloatArgOffset uint64
+ curIntRetArgOffset uint64
+ curFloatRetArgOffset uint64
+
+ intArgRegisters []RegisterName
+ floatArgRegisters []RegisterName
+ intRetArgRegisters []RegisterName
+ floatRetArgRegisters []RegisterName
+}
+
+func newGolangRegisterLocator() *GolangRegisterLocator {
+ intRegisters := []RegisterName{
+ RegisterNameRAX, RegisterNameRBX, RegisterNameRCX,
+ RegisterNameRDI, RegisterNameRSI, RegisterNameR8,
+ RegisterNameR9, RegisterNameR10, RegisterNameR11,
+ }
+ floatRegisters := []RegisterName{
+ RegisterNameXMM0, RegisterNameXMM1, RegisterNameXMM2,
+ RegisterNameXMM3, RegisterNameXMM4, RegisterNameXMM5,
+ RegisterNameXMM6, RegisterNameXMM7, RegisterNameXMM8,
+ RegisterNameXMM9, RegisterNameXMM10, RegisterNameXMM11,
+ RegisterNameXMM12, RegisterNameXMM13, RegisterNameXMM14,
+ }
+ return &GolangRegisterLocator{
+ intArgRegisters: intRegisters,
+ floatArgRegisters: floatRegisters,
+ intRetArgRegisters: intRegisters,
+ floatRetArgRegisters: floatRegisters,
+ }
+}
+
+func (g *GolangRegisterLocator) GetLocation(typeClass TypeClass, typeSize,
alignmentSize uint64, primitivesCount int,
+ isRetArg bool) (*ArgLocation, error) {
+ var registers []RegisterName
+ var offset *uint64
+ if typeClass == TypeClassInteger {
+ if isRetArg {
+ registers = g.intRetArgRegisters
+ offset = &g.curIntRetArgOffset
+ } else {
+ registers = g.intArgRegisters
+ offset = &g.curIntArgOffset
+ }
+ } else if typeClass == TypeClassFloat {
+ if isRetArg {
+ registers = g.floatRetArgRegisters
+ offset = &g.curFloatRetArgOffset
+ } else {
+ registers = g.floatArgRegisters
+ offset = &g.curFloatArgOffset
+ }
+ } else {
+ return nil, fmt.Errorf("unsupport type classs for getting
location, type class: %d", typeClass)
+ }
+
+ result := &ArgLocation{}
+ if primitivesCount <= len(registers) {
+ if typeClass == TypeClassInteger {
+ result.Type = ArgLocationTypeRegister
+ } else {
+ result.Type = ArgLocationTypeRegisterFP
+ }
+ result.Offset = *offset
+ *offset += uint64(primitivesCount * 8)
+ } else {
+ g.curStackOffset = snapUpToMultiple(g.curStackOffset,
alignmentSize)
+ result.Type = ArgLocationTypeStack
+ result.Offset = g.curStackOffset
+
+ g.curStackOffset += typeSize
+ }
+ return result, nil
+}
+
+type GolangStackLocator struct {
+ curStackOffset uint64
+}
+
+func (g *GolangStackLocator) GetLocation(_ TypeClass, _, alignmentSize uint64,
_ int, _ bool) (*ArgLocation, error) {
+ g.curStackOffset = snapUpToMultiple(g.curStackOffset, alignmentSize)
+ result := &ArgLocation{}
+ result.Type = ArgLocationTypeStack
+ result.Offset = g.curStackOffset
+ return result, nil
+}
+
+type UnknownLocator struct {
+ language int
+}
+
+func (u *UnknownLocator) GetLocation(typeClass TypeClass, typeSize,
alignmentSize uint64, primitivesCount int, isRetArg bool) (*ArgLocation, error)
{
+ return nil, fmt.Errorf("unknown locator for language: %d", u.language)
+}
+
+func snapUpToMultiple(curSize, alignmentSize uint64) uint64 {
+ return (curSize + (alignmentSize-1)/alignmentSize) * alignmentSize
+}
diff --git a/pkg/tools/elf/dwarf.go b/pkg/tools/elf/dwarf.go
new file mode 100644
index 0000000..a899842
--- /dev/null
+++ b/pkg/tools/elf/dwarf.go
@@ -0,0 +1,83 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package elf
+
+import (
+ "debug/dwarf"
+ "encoding/binary"
+ "fmt"
+)
+
+var ReaderLanguageGolang = 22
+
+type DwarfReader struct {
+ elfByteOrder binary.ByteOrder
+
+ producer string
+ language int
+ functions map[string]*FunctionInfo
+ structures map[string]*StructureInfo
+}
+
+func (f *File) NewDwarfReader(attrNames ...string) (*DwarfReader, error) {
+ data, err := f.realFile.DWARF()
+ if err != nil {
+ return nil, err
+ }
+ reader := &DwarfReader{}
+ if err := reader.init(data, attrNames); err != nil {
+ return nil, err
+ }
+ return reader, nil
+}
+
+func (r *DwarfReader) GetFunction(name string) *FunctionInfo {
+ return r.functions[name]
+}
+
+func (r *DwarfReader) GetStructure(name string) *StructureInfo {
+ return r.structures[name]
+}
+
+func (r *DwarfReader) init(data *dwarf.Data, readAttrNames []string) error {
+ r.functions = make(map[string]*FunctionInfo)
+ r.structures = make(map[string]*StructureInfo)
+
+ reader := data.Reader()
+ r.elfByteOrder = reader.ByteOrder()
+ for {
+ entry, err := reader.Next()
+ if err != nil {
+ return fmt.Errorf("read dwarf error: %v", err)
+ }
+ if entry == nil {
+ break
+ }
+
+ if err := r.processProducer(data, entry); err != nil {
+ return err
+ }
+ if err := r.processFunctions(readAttrNames, data, entry); err
!= nil {
+ return err
+ }
+ if err := r.processStructure(readAttrNames, data, entry); err
!= nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/pkg/tools/elf/dwarf_init.go b/pkg/tools/elf/dwarf_init.go
new file mode 100644
index 0000000..cdfce40
--- /dev/null
+++ b/pkg/tools/elf/dwarf_init.go
@@ -0,0 +1,293 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package elf
+
+import (
+ "debug/dwarf"
+ "fmt"
+)
+
+func (r *DwarfReader) processProducer(_ *dwarf.Data, entry *dwarf.Entry) error
{
+ if entry.Tag != dwarf.TagCompileUnit {
+ return nil
+ }
+
+ producer, ok := entry.Val(dwarf.AttrProducer).(string)
+ if !ok {
+ return fmt.Errorf("the producer field not exists")
+ }
+ language, ok := entry.Val(dwarf.AttrLanguage).(int64)
+ if !ok {
+ return fmt.Errorf("the language field not exists")
+ }
+ r.producer = producer
+ r.language = int(language)
+ return nil
+}
+
+func (r *DwarfReader) processFunctions(funcNames []string, data *dwarf.Data,
entry *dwarf.Entry) error {
+ if entry.Tag != dwarf.TagSubprogram {
+ return nil
+ }
+
+ name, ok := entry.Val(dwarf.AttrName).(string)
+ if !ok {
+ return nil
+ }
+
+ // the function is need to read
+ found := false
+ for _, n := range funcNames {
+ if n == name {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return nil
+ }
+ args, err := r.getFunctionArgs(data, entry)
+ if err != nil {
+ return err
+ }
+
+ r.functions[name] = &FunctionInfo{
+ name: name,
+ args: args,
+ }
+ return nil
+}
+
+func (r *DwarfReader) processStructure(names []string, data *dwarf.Data, entry
*dwarf.Entry) error {
+ if entry.Tag != dwarf.TagStructType {
+ return nil
+ }
+
+ name, ok := entry.Val(dwarf.AttrName).(string)
+ if !ok {
+ return nil
+ }
+
+ found := false
+ for _, n := range names {
+ if n == name {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return nil
+ }
+
+ fields, err := r.getStructureFields(data, entry)
+ if err != nil {
+ return err
+ }
+
+ r.structures[name] = &StructureInfo{
+ name: name,
+ fields: fields,
+ }
+ return nil
+}
+
+func (r *DwarfReader) entryType(data *dwarf.Data, entry *dwarf.Entry)
(dwarf.Type, error) {
+ off, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
+ if !ok {
+ return nil, nil
+ }
+ return data.Type(off)
+}
+
+func (r *DwarfReader) IsRetArgs(entry *dwarf.Entry) (bool, error) {
+ isRet, ok := entry.Val(dwarf.AttrVarParam).(bool)
+ if !ok {
+ return false, nil
+ }
+ return isRet, nil
+}
+
+func (r *DwarfReader) getStructureFields(data *dwarf.Data, entry *dwarf.Entry)
([]*StructureFieldInfo, error) {
+ reader := data.Reader()
+
+ reader.Seek(entry.Offset)
+ _, err := reader.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ res := make([]*StructureFieldInfo, 0)
+ for {
+ child, err := reader.Next()
+ if err != nil {
+ return nil, err
+ }
+ if child == nil || child.Tag == 0 {
+ break
+ }
+
+ if child.Tag != dwarf.TagMember {
+ continue
+ }
+
+ name, ok := child.Val(dwarf.AttrName).(string)
+ if !ok {
+ continue
+ }
+ offset, ok := child.Val(dwarf.AttrDataMemberLoc).(int64)
+ if !ok {
+ continue
+ }
+
+ field := &StructureFieldInfo{
+ Name: name,
+ Offset: offset,
+ }
+ res = append(res, field)
+ }
+ return res, nil
+}
+
+func (r *DwarfReader) getFunctionArgs(data *dwarf.Data, entry *dwarf.Entry)
(map[string]*FunctionArgsInfo, error) {
+ reader := data.Reader()
+
+ reader.Seek(entry.Offset)
+ _, err := reader.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ locator := NewArgumentLocator(r)
+ args := make(map[string]*FunctionArgsInfo)
+ for {
+ child, err := reader.Next()
+ if err != nil {
+ return nil, err
+ }
+ if child == nil || child.Tag == 0 {
+ break
+ }
+
+ if child.Tag != dwarf.TagFormalParameter {
+ continue
+ }
+ name, ok := child.Val(dwarf.AttrName).(string)
+ if !ok {
+ continue
+ }
+ if existsArgs := args[name]; existsArgs != nil {
+ continue
+ }
+
+ curArgs := &FunctionArgsInfo{}
+ // data type
+ dtyp, err := r.entryType(data, child)
+ if err != nil {
+ return nil, err
+ }
+ curArgs.tp = dtyp
+
+ // Is return value
+ if r.language == ReaderLanguageGolang {
+ isRet, err1 := r.IsRetArgs(child)
+ if err1 != nil {
+ return nil, err1
+ }
+ curArgs.IsRet = isRet
+ }
+
+ // get location
+ typeClass := r.getArgTypeClass(child, dtyp)
+ byteSize := r.getArgTypeByteSize(child, dtyp)
+ alignmentByteSize := r.getArgAlignmentByteSize(child, dtyp)
+ primitiveFieldCount := r.getPrimitiveFieldCount(child, dtyp)
+ location, err := locator.GetLocation(typeClass, byteSize,
alignmentByteSize, primitiveFieldCount, curArgs.IsRet)
+ if err != nil {
+ return nil, err
+ }
+ curArgs.Location = location
+
+ args[name] = curArgs
+ }
+
+ return args, nil
+}
+
+func (r *DwarfReader) getArgTypeClass(_ *dwarf.Entry, tp dwarf.Type) TypeClass
{
+ switch val := tp.(type) {
+ case *dwarf.FloatType:
+ return TypeClassFloat
+ case *dwarf.StructType:
+ res := TypeClassNone
+ for _, field := range val.Field {
+ memberType := r.getArgTypeClass(nil, field.Type)
+ res = res.Combine(memberType)
+ }
+ return res
+ default:
+ return TypeClassInteger
+ }
+}
+
+func (r *DwarfReader) getArgTypeByteSize(_ *dwarf.Entry, tp dwarf.Type) uint64
{
+ basicType, ok := tp.(*dwarf.BasicType)
+ if ok {
+ return uint64(basicType.ByteSize)
+ }
+
+ switch val := tp.(type) {
+ case *dwarf.StructType:
+ return uint64(val.ByteSize)
+ default:
+ return 8
+ }
+}
+
+func (r *DwarfReader) getArgAlignmentByteSize(_ *dwarf.Entry, tp dwarf.Type)
uint64 {
+ basicType, ok := tp.(*dwarf.BasicType)
+ if ok {
+ return uint64(basicType.ByteSize)
+ }
+
+ switch val := tp.(type) {
+ case *dwarf.StructType:
+ var maxSize uint64 = 1
+ for _, field := range val.Field {
+ curSize := r.getArgAlignmentByteSize(nil, field.Type)
+ if curSize > maxSize {
+ maxSize = curSize
+ }
+ }
+ return maxSize
+ default:
+ return 8
+ }
+}
+
+func (r *DwarfReader) getPrimitiveFieldCount(_ *dwarf.Entry, tp dwarf.Type)
int {
+ structType, ok := tp.(*dwarf.StructType)
+ if ok {
+ totalCount := 0
+ for _, field := range structType.Field {
+ totalCount += r.getPrimitiveFieldCount(nil, field.Type)
+ }
+ return totalCount
+ }
+
+ return 1
+}
diff --git a/pkg/tools/elf/elf.go b/pkg/tools/elf/elf.go
new file mode 100644
index 0000000..57ece5d
--- /dev/null
+++ b/pkg/tools/elf/elf.go
@@ -0,0 +1,107 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package elf
+
+import (
+ "debug/elf"
+ "fmt"
+ "os"
+)
+
+type File struct {
+ path string
+ realFile *elf.File
+}
+
+type Symbol struct {
+ Name string
+ Location uint64
+ Size uint64
+}
+
+func NewFile(path string) (*File, error) {
+ f, err := elf.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ return &File{
+ path: path,
+ realFile: f,
+ }, nil
+}
+
+func (f *File) Close() error {
+ return f.realFile.Close()
+}
+
+func (f *File) FindSymbol(name string) *Symbol {
+ symbols, _ := f.realFile.Symbols()
+ dynamicSymbols, _ := f.realFile.DynamicSymbols()
+ if len(symbols) == 0 && len(dynamicSymbols) == 0 {
+ return nil
+ }
+ symbols = append(symbols, dynamicSymbols...)
+ for _, s := range symbols {
+ if s.Name == name {
+ return &Symbol{
+ Name: name,
+ Location: s.Value,
+ Size: s.Size,
+ }
+ }
+ }
+ return nil
+}
+
+func (f *File) ReadSymbolData(section string, offset, size uint64) ([]byte,
error) {
+ elfSection := f.realFile.Section(section)
+ if elfSection == nil {
+ return nil, fmt.Errorf("could not found the \"%s\" section in
elf file", section)
+ }
+
+ dataOffset := offset - elfSection.Addr + elfSection.Offset
+ realFile, err := os.Open(f.path)
+ if err != nil {
+ return nil, err
+ }
+ defer realFile.Close()
+ _, err = realFile.Seek(int64(dataOffset), 0)
+ if err != nil {
+ return nil, fmt.Errorf("seek file error: %v", err)
+ }
+
+ buffer := make([]byte, size)
+ _, err = realFile.Read(buffer)
+ if err != nil {
+ return nil, fmt.Errorf("reading symbol data error: %v", err)
+ }
+ return buffer, nil
+}
+
+func (f *File) FindBaseOffsetForAttach(symbolLocation uint64) uint64 {
+ for _, prog := range f.realFile.Progs {
+ if prog.Type != elf.PT_LOAD || (prog.Flags&elf.PF_X) == 0 {
+ continue
+ }
+
+ if prog.Vaddr <= symbolLocation && symbolLocation <
(prog.Vaddr+prog.Memsz) {
+ return symbolLocation - prog.Vaddr + prog.Off
+ }
+ }
+ return 0
+}
diff --git a/pkg/tools/elf/enums.go b/pkg/tools/elf/enums.go
new file mode 100644
index 0000000..9e4b967
--- /dev/null
+++ b/pkg/tools/elf/enums.go
@@ -0,0 +1,87 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package elf
+
+type TypeClass int
+
+const (
+ TypeClassNone TypeClass = 0
+ TypeClassInteger TypeClass = 1
+ TypeClassFloat TypeClass = 2
+ TypeClassMixed TypeClass = 3
+)
+
+func (c TypeClass) Combine(other TypeClass) TypeClass {
+ if c == TypeClassMixed || other == TypeClassMixed {
+ return TypeClassMixed
+ }
+ if c == TypeClassNone {
+ return other
+ }
+ if other == TypeClassNone {
+ return c
+ }
+
+ if c != other {
+ return TypeClassMixed
+ }
+ return c
+}
+
+type LocationType uint64
+
+const (
+ ArgLocationTypeUnknown LocationType = 0
+ ArgLocationTypeStack LocationType = 1 // frame stack pointer
+ ArgLocationTypeStackBP LocationType = 2 // frame base pointer
+ ArgLocationTypeRegister LocationType = 3 // integer register
+ ArgLocationTypeRegisterFP LocationType = 4 // float-point register
+)
+
+type RegisterName int
+
+const (
+ // for int type class
+ RegisterNameRAX RegisterName = 0
+ RegisterNameRBX RegisterName = 1
+ RegisterNameRCX RegisterName = 2
+ RegisterNameRDX RegisterName = 3
+ RegisterNameRDI RegisterName = 4
+ RegisterNameRSI RegisterName = 5
+ RegisterNameR8 RegisterName = 6
+ RegisterNameR9 RegisterName = 7
+ RegisterNameR10 RegisterName = 8
+ RegisterNameR11 RegisterName = 9
+
+ // for float type class
+ RegisterNameXMM0 RegisterName = 100
+ RegisterNameXMM1 RegisterName = 101
+ RegisterNameXMM2 RegisterName = 102
+ RegisterNameXMM3 RegisterName = 103
+ RegisterNameXMM4 RegisterName = 104
+ RegisterNameXMM5 RegisterName = 105
+ RegisterNameXMM6 RegisterName = 106
+ RegisterNameXMM7 RegisterName = 107
+ RegisterNameXMM8 RegisterName = 108
+ RegisterNameXMM9 RegisterName = 109
+ RegisterNameXMM10 RegisterName = 110
+ RegisterNameXMM11 RegisterName = 111
+ RegisterNameXMM12 RegisterName = 112
+ RegisterNameXMM13 RegisterName = 113
+ RegisterNameXMM14 RegisterName = 114
+)
diff --git a/pkg/tools/elf/function.go b/pkg/tools/elf/function.go
new file mode 100644
index 0000000..1882974
--- /dev/null
+++ b/pkg/tools/elf/function.go
@@ -0,0 +1,39 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package elf
+
+import "debug/dwarf"
+
+type FunctionInfo struct {
+ name string
+ args map[string]*FunctionArgsInfo
+}
+
+func (f *FunctionInfo) Name() string {
+ return f.name
+}
+
+func (f *FunctionInfo) Args(name string) *FunctionArgsInfo {
+ return f.args[name]
+}
+
+type FunctionArgsInfo struct {
+ tp dwarf.Type
+ IsRet bool
+ Location *ArgLocation
+}
diff --git a/pkg/tools/elf/structure.go b/pkg/tools/elf/structure.go
new file mode 100644
index 0000000..1b5cf6b
--- /dev/null
+++ b/pkg/tools/elf/structure.go
@@ -0,0 +1,37 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package elf
+
+type StructureInfo struct {
+ name string
+ fields []*StructureFieldInfo
+}
+
+func (s *StructureInfo) GetField(name string) *StructureFieldInfo {
+ for _, f := range s.fields {
+ if f.Name == name {
+ return f
+ }
+ }
+ return nil
+}
+
+type StructureFieldInfo struct {
+ Name string
+ Offset int64
+}
diff --git a/pkg/tools/profiling/api.go b/pkg/tools/profiling/api.go
index 32f2fb8..0c25e2b 100644
--- a/pkg/tools/profiling/api.go
+++ b/pkg/tools/profiling/api.go
@@ -62,6 +62,7 @@ type ModuleRange struct {
type Symbol struct {
Name string
Location uint64
+ Size uint64
}
type StatFinder interface {
diff --git a/pkg/tools/profiling/go_library.go
b/pkg/tools/profiling/go_library.go
index 2257b2a..f6a7cf0 100644
--- a/pkg/tools/profiling/go_library.go
+++ b/pkg/tools/profiling/go_library.go
@@ -62,7 +62,7 @@ func (l *GoLibrary) AnalyzeSymbols(filePath string)
([]*Symbol, error) {
// adapt symbol struct
data := make([]*Symbol, len(symbols))
for i, sym := range symbols {
- data[i] = &Symbol{Name: sym.Name, Location: sym.Value}
+ data[i] = &Symbol{Name: sym.Name, Location: sym.Value, Size:
sym.Size}
}
sort.SliceStable(data, func(i, j int) bool {
diff --git a/pkg/tools/profiling/kernel.go b/pkg/tools/profiling/kernel.go
index 95cee54..51a75c4 100644
--- a/pkg/tools/profiling/kernel.go
+++ b/pkg/tools/profiling/kernel.go
@@ -63,6 +63,7 @@ func (k *KernelFinder) Analyze(filepath string) (*Info,
error) {
symbols = append(symbols, &Symbol{
Name: info[2],
Location: atoi,
+ Size: 0,
})
}