aboutsummaryrefslogtreecommitdiff
path: root/gdb/nat/linux-btrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/nat/linux-btrace.c')
-rw-r--r--gdb/nat/linux-btrace.c83
1 files changed, 51 insertions, 32 deletions
diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c
index 51725ff..08478d8 100644
--- a/gdb/nat/linux-btrace.c
+++ b/gdb/nat/linux-btrace.c
@@ -24,6 +24,9 @@
#include "common-regcache.h"
#include "gdb_wait.h"
#include "x86-cpuid.h"
+#include "filestuff.h"
+
+#include <inttypes.h>
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
@@ -36,7 +39,6 @@
#include "nat/gdb_ptrace.h"
#include <sys/types.h>
#include <signal.h>
-#include <sys/utsname.h>
/* A branch trace record in perf_event. */
struct perf_event_bts
@@ -189,56 +191,75 @@ perf_event_pt_event_type (int *type)
return -1;
}
-static int
-linux_determine_kernel_ptr_bits (void)
+/* Try to determine the start address of the Linux kernel. */
+
+static uint64_t
+linux_determine_kernel_start (void)
{
- struct utsname utsn;
- int errcode;
+ static uint64_t kernel_start;
+ static int cached;
+ FILE *file;
- memset (&utsn, 0, sizeof (utsn));
+ if (cached != 0)
+ return kernel_start;
- errcode = uname (&utsn);
- if (errcode < 0)
- return 0;
+ cached = 1;
- /* We only need to handle the 64-bit host case, here. For 32-bit host,
- the pointer size can be filled in later based on the inferior. */
- if (strcmp (utsn.machine, "x86_64") == 0)
- return 64;
+ file = gdb_fopen_cloexec ("/proc/kallsyms", "r");
+ if (file == NULL)
+ return kernel_start;
- return 0;
+ while (!feof (file))
+ {
+ char buffer[1024], symbol[8], *line;
+ uint64_t addr;
+ int match;
+
+ line = fgets (buffer, sizeof (buffer), file);
+ if (line == NULL)
+ break;
+
+ match = sscanf (line, "%" SCNx64 " %*[tT] %7s", &addr, symbol);
+ if (match != 2)
+ continue;
+
+ if (strcmp (symbol, "_text") == 0)
+ {
+ kernel_start = addr;
+ break;
+ }
+ }
+
+ fclose (file);
+
+ return kernel_start;
}
/* Check whether an address is in the kernel. */
static inline int
-perf_event_is_kernel_addr (const struct btrace_target_info *tinfo,
- uint64_t addr)
+perf_event_is_kernel_addr (uint64_t addr)
{
- uint64_t mask;
-
- /* If we don't know the size of a pointer, we can't check. Let's assume it's
- not a kernel address in this case. */
- if (tinfo->ptr_bits == 0)
- return 0;
+ uint64_t kernel_start;
- /* A bit mask for the most significant bit in an address. */
- mask = (uint64_t) 1 << (tinfo->ptr_bits - 1);
+ kernel_start = linux_determine_kernel_start ();
+ if (kernel_start != 0ull)
+ return (addr >= kernel_start);
- /* Check whether the most significant bit in the address is set. */
- return (addr & mask) != 0;
+ /* If we don't know the kernel's start address, let's check the most
+ significant bit. This will work at least for 64-bit kernels. */
+ return ((addr & (1ull << 63)) != 0);
}
/* Check whether a perf event record should be skipped. */
static inline int
-perf_event_skip_bts_record (const struct btrace_target_info *tinfo,
- const struct perf_event_bts *bts)
+perf_event_skip_bts_record (const struct perf_event_bts *bts)
{
/* The hardware may report branches from kernel into user space. Branches
from user into kernel space will be suppressed. We filter the former to
provide a consistent branch trace excluding kernel. */
- return perf_event_is_kernel_addr (tinfo, bts->from);
+ return perf_event_is_kernel_addr (bts->from);
}
/* Perform a few consistency checks on a perf event sample record. This is
@@ -335,7 +356,7 @@ perf_event_read_bts (struct btrace_target_info* tinfo, const uint8_t *begin,
break;
}
- if (perf_event_skip_bts_record (tinfo, &psample->bts))
+ if (perf_event_skip_bts_record (&psample->bts))
continue;
/* We found a valid sample, so we can complete the current block. */
@@ -649,7 +670,6 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf)
tinfo = XCNEW (struct btrace_target_info);
tinfo->ptid = ptid;
- tinfo->ptr_bits = linux_determine_kernel_ptr_bits ();
tinfo->conf.format = BTRACE_FORMAT_BTS;
bts = &tinfo->variant.bts;
@@ -782,7 +802,6 @@ linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
tinfo = XCNEW (struct btrace_target_info);
tinfo->ptid = ptid;
- tinfo->ptr_bits = 0;
tinfo->conf.format = BTRACE_FORMAT_PT;
pt = &tinfo->variant.pt;