diff options
author | Markus Metzger <markus.t.metzger@intel.com> | 2014-01-24 13:45:47 +0100 |
---|---|---|
committer | Markus Metzger <markus.t.metzger@intel.com> | 2015-07-02 12:49:32 +0200 |
commit | b20a652466ea6e62e7d056188b79a0677a29f46e (patch) | |
tree | 83874980f42bad201cc6e22c78608cb32237b58d /gdb/nat | |
parent | 58bfce93438e1f936d4547bb9659b7d096e2823f (diff) | |
download | gdb-b20a652466ea6e62e7d056188b79a0677a29f46e.zip gdb-b20a652466ea6e62e7d056188b79a0677a29f46e.tar.gz gdb-b20a652466ea6e62e7d056188b79a0677a29f46e.tar.bz2 |
btrace: support Intel(R) Processor Trace
Adds a new command "record btrace pt" to configure the kernel to use
Intel(R) Processor Trace instead of Branch Trace Strore.
The "record btrace" command chooses the tracing format automatically.
Intel(R) Processor Trace support requires Linux 4.1 and libipt.
gdb/
* NEWS: Announce new commands "record btrace pt" and "record pt".
Announce new options "set|show record btrace pt buffer-size".
* btrace.c: Include "rsp-low.h".
Include "inttypes.h".
(btrace_add_pc): Add forward declaration.
(pt_reclassify_insn, ftrace_add_pt, btrace_pt_readmem_callback)
(pt_translate_cpu_vendor, btrace_finalize_ftrace_pt)
(btrace_compute_ftrace_pt): New.
(btrace_compute_ftrace): Support BTRACE_FORMAT_PT.
(check_xml_btrace_version): Update version check.
(parse_xml_raw, parse_xml_btrace_pt_config_cpu)
(parse_xml_btrace_pt_raw, parse_xml_btrace_pt)
(btrace_pt_config_cpu_attributes, btrace_pt_config_children)
(btrace_pt_children): New.
(btrace_children): Add support for "pt".
(parse_xml_btrace_conf_pt, btrace_conf_pt_attributes): New.
(btrace_conf_children): Add support for "pt".
* btrace.h: Include "intel-pt.h".
(btrace_pt_error): New.
* common/btrace-common.c (btrace_format_string, btrace_data_fini)
(btrace_data_empty): Support BTRACE_FORMAT_PT.
* common/btrace-common.h (btrace_format): Add BTRACE_FORMAT_PT.
(struct btrace_config_pt): New.
(struct btrace_config)<pt>: New.
(struct btrace_data_pt_config, struct btrace_data_pt): New.
(struct btrace_data)<pt>: New.
* features/btrace-conf.dtd (btrace-conf)<pt>: New.
(pt): New.
* features/btrace.dtd (btrace)<pt>: New.
(pt, pt-config, cpu): New.
* nat/linux-btrace.c (perf_event_read, perf_event_read_all)
(perf_event_pt_event_type, kernel_supports_pt)
(linux_supports_pt): New.
(linux_supports_btrace): Support BTRACE_FORMAT_PT.
(linux_enable_bts): Free tinfo on error.
(linux_enable_pt): New.
(linux_enable_btrace): Support BTRACE_FORMAT_PT.
(linux_disable_pt): New.
(linux_disable_btrace): Support BTRACE_FORMAT_PT.
(linux_fill_btrace_pt_config, linux_read_pt): New.
(linux_read_btrace): Support BTRACE_FORMAT_PT.
* nat/linux-btrace.h (struct btrace_tinfo_pt): New.
(struct btrace_target_info)<pt>: New.
* record-btrace.c (set_record_btrace_pt_cmdlist)
(show_record_btrace_pt_cmdlist): New.
(record_btrace_print_pt_conf): New.
(record_btrace_print_conf): Support BTRACE_FORMAT_PT.
(btrace_ui_out_decode_error): Support BTRACE_FORMAT_PT.
(cmd_record_btrace_pt_start): New.
(cmd_record_btrace_start): Support BTRACE_FORMAT_PT.
(cmd_set_record_btrace_pt, cmd_show_record_btrace_pt): New.
(_initialize_record_btrace): Add new commands.
* remote.c (PACKET_Qbtrace_pt, PACKET_Qbtrace_conf_pt_size): New.
(remote_protocol_features): Add "Qbtrace:pt".
Add "Qbtrace-conf:pt:size".
(remote_supports_btrace): Support BTRACE_FORMAT_PT.
(btrace_sync_conf): Support PACKET_Qbtrace_conf_pt_size.
(remote_enable_btrace): Support BTRACE_FORMAT_PT.
(_initialize_remote): Add new commands.
gdbserver/
* linux-low.c: Include "rsp-low.h"
(linux_low_encode_pt_config, linux_low_encode_raw): New.
(linux_low_read_btrace): Support BTRACE_FORMAT_PT.
(linux_low_btrace_conf): Support BTRACE_FORMAT_PT.
(handle_btrace_enable_pt): New.
(handle_btrace_general_set): Support "pt".
(handle_btrace_conf_general_set): Support "pt:size".
doc/
* gdb.texinfo (Process Record and Replay): Spell out that variables
and registers are not available during btrace replay.
Describe the new "record btrace pt" command.
Describe the new "set|show record btrace pt buffer-size" options.
(General Query Packets): Describe the new Qbtrace:pt and
Qbtrace-conf:pt:size packets.
Expand "bts" to "Branch Trace Store".
Update the branch trace DTD.
Diffstat (limited to 'gdb/nat')
-rw-r--r-- | gdb/nat/linux-btrace.c | 377 | ||||
-rw-r--r-- | gdb/nat/linux-btrace.h | 19 |
2 files changed, 395 insertions, 1 deletions
diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c index cd7f3d1..ddd79a3 100644 --- a/gdb/nat/linux-btrace.c +++ b/gdb/nat/linux-btrace.c @@ -106,6 +106,87 @@ perf_event_new_data (const struct perf_event_buffer *pev) This is the same as the size of a pointer for the inferior process except when a 32-bit inferior is running on a 64-bit OS. */ +/* Copy the last SIZE bytes from PEV ending at DATA_HEAD and return a pointer + to the memory holding the copy. + The caller is responsible for freeing the memory. */ + +static gdb_byte * +perf_event_read (const struct perf_event_buffer *pev, unsigned long data_head, + unsigned long size) +{ + const gdb_byte *begin, *end, *start, *stop; + gdb_byte *buffer; + unsigned long data_tail, buffer_size; + + if (size == 0) + return NULL; + + gdb_assert (size <= data_head); + data_tail = data_head - size; + + buffer_size = pev->size; + begin = pev->mem; + start = begin + data_tail % buffer_size; + stop = begin + data_head % buffer_size; + + buffer = xmalloc (size); + + if (start < stop) + memcpy (buffer, start, stop - start); + else + { + end = begin + buffer_size; + + memcpy (buffer, start, end - start); + memcpy (buffer + (end - start), begin, stop - begin); + } + + return buffer; +} + +/* Copy the perf event buffer data from PEV. + Store a pointer to the copy into DATA and its size in SIZE. */ + +static void +perf_event_read_all (struct perf_event_buffer *pev, gdb_byte **data, + unsigned long *psize) +{ + unsigned long data_head, size; + + data_head = *pev->data_head; + + size = pev->size; + if (data_head < size) + size = data_head; + + *data = perf_event_read (pev, data_head, size); + *psize = size; + + pev->last_head = data_head; +} + +/* Determine the event type. + Returns zero on success and fills in TYPE; returns -1 otherwise. */ + +static int +perf_event_pt_event_type (int *type) +{ + FILE *file; + int found; + + file = fopen ("/sys/bus/event_source/devices/intel_pt/type", "r"); + if (file == NULL) + return -1; + + found = fscanf (file, "%d", type); + + fclose (file); + + if (found == 1) + return 0; + return -1; +} + static int linux_determine_kernel_ptr_bits (void) { @@ -358,6 +439,93 @@ kernel_supports_bts (void) } } +/* Check whether the kernel supports Intel(R) Processor Trace. */ + +static int +kernel_supports_pt (void) +{ + struct perf_event_attr attr; + pid_t child, pid; + int status, file, type; + + errno = 0; + child = fork (); + switch (child) + { + case -1: + warning (_("test pt: cannot fork: %s."), strerror (errno)); + return 0; + + case 0: + status = ptrace (PTRACE_TRACEME, 0, NULL, NULL); + if (status != 0) + { + warning (_("test pt: cannot PTRACE_TRACEME: %s."), + strerror (errno)); + _exit (1); + } + + status = raise (SIGTRAP); + if (status != 0) + { + warning (_("test pt: cannot raise SIGTRAP: %s."), + strerror (errno)); + _exit (1); + } + + _exit (1); + + default: + pid = waitpid (child, &status, 0); + if (pid != child) + { + warning (_("test pt: bad pid %ld, error: %s."), + (long) pid, strerror (errno)); + return 0; + } + + if (!WIFSTOPPED (status)) + { + warning (_("test pt: expected stop. status: %d."), + status); + return 0; + } + + status = perf_event_pt_event_type (&type); + if (status != 0) + file = -1; + else + { + memset (&attr, 0, sizeof (attr)); + + attr.size = sizeof (attr); + attr.type = type; + attr.exclude_kernel = 1; + attr.exclude_hv = 1; + attr.exclude_idle = 1; + + file = syscall (SYS_perf_event_open, &attr, child, -1, -1, 0); + if (file >= 0) + close (file); + } + + kill (child, SIGKILL); + ptrace (PTRACE_KILL, child, NULL, NULL); + + pid = waitpid (child, &status, 0); + if (pid != child) + { + warning (_("test pt: bad pid %ld, error: %s."), + (long) pid, strerror (errno)); + if (!WIFSIGNALED (status)) + warning (_("test pt: expected killed. status: %d."), + status); + } + + return (file >= 0); + } +} + /* Check whether an Intel cpu supports BTS. */ static int @@ -428,6 +596,24 @@ linux_supports_bts (void) return cached > 0; } +/* Check whether the linux target supports Intel(R) Processor Trace. */ + +static int +linux_supports_pt (void) +{ + static int cached; + + if (cached == 0) + { + if (!kernel_supports_pt ()) + cached = -1; + else + cached = 1; + } + + return cached > 0; +} + /* See linux-btrace.h. */ int @@ -440,6 +626,9 @@ linux_supports_btrace (struct target_ops *ops, enum btrace_format format) case BTRACE_FORMAT_BTS: return linux_supports_bts (); + + case BTRACE_FORMAT_PT: + return linux_supports_pt (); } internal_error (__FILE__, __LINE__, _("Unknown branch trace format")); @@ -482,7 +671,7 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf) errno = 0; bts->file = syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0); if (bts->file < 0) - goto err; + goto err_out; /* Convert the requested size in bytes to pages (rounding up). */ pages = (((unsigned long long) conf->size) + PAGE_SIZE - 1) / PAGE_SIZE; @@ -531,11 +720,127 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf) /* We were not able to allocate any buffer. */ close (bts->file); + err_out: + xfree (tinfo); + return NULL; +} + +#if defined (PERF_ATTR_SIZE_VER5) + +/* Enable branch tracing in Intel(R) Processor Trace format. */ + +static struct btrace_target_info * +linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf) +{ + struct perf_event_mmap_page *header; + struct btrace_target_info *tinfo; + struct btrace_tinfo_pt *pt; + unsigned long long pages, size; + int pid, pg, errcode, type; + + if (conf->size == 0) + return NULL; + + errcode = perf_event_pt_event_type (&type); + if (errcode != 0) + return NULL; + + pid = ptid_get_lwp (ptid); + if (pid == 0) + pid = ptid_get_pid (ptid); + + tinfo = xzalloc (sizeof (*tinfo)); + tinfo->ptid = ptid; + tinfo->ptr_bits = 0; + + tinfo->conf.format = BTRACE_FORMAT_PT; + pt = &tinfo->variant.pt; + + pt->attr.size = sizeof (pt->attr); + pt->attr.type = type; + + pt->attr.exclude_kernel = 1; + pt->attr.exclude_hv = 1; + pt->attr.exclude_idle = 1; + + errno = 0; + pt->file = syscall (SYS_perf_event_open, &pt->attr, pid, -1, -1, 0); + if (pt->file < 0) + goto err; + + /* Allocate the configuration page. */ + header = mmap (NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + pt->file, 0); + if (header == MAP_FAILED) + goto err_file; + + header->aux_offset = header->data_offset + header->data_size; + + /* Convert the requested size in bytes to pages (rounding up). */ + pages = (((unsigned long long) conf->size) + PAGE_SIZE - 1) / PAGE_SIZE; + /* We need at least one page. */ + if (pages == 0) + pages = 1; + + /* The buffer size can be requested in powers of two pages. Adjust PAGES + to the next power of two. */ + for (pg = 0; pages != (1u << pg); ++pg) + if ((pages & (1u << pg)) != 0) + pages += (1u << pg); + + /* We try to allocate the requested size. + If that fails, try to get as much as we can. */ + for (; pages > 0; pages >>= 1) + { + size_t length; + + size = pages * PAGE_SIZE; + length = size; + + /* Check for overflows. */ + if ((unsigned long long) length < size) + continue; + + header->aux_size = size; + + pt->pt.mem = mmap (NULL, length, PROT_READ, MAP_SHARED, pt->file, + header->aux_offset); + if (pt->pt.mem != MAP_FAILED) + break; + } + + if (pages == 0) + goto err_conf; + + pt->header = header; + pt->pt.size = size; + pt->pt.data_head = &header->aux_head; + + tinfo->conf.pt.size = size; + return tinfo; + + err_conf: + munmap((void *) header, PAGE_SIZE); + + err_file: + close (pt->file); + err: xfree (tinfo); return NULL; } +#else /* !defined (PERF_ATTR_SIZE_VER5) */ + +static struct btrace_target_info * +linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf) +{ + errno = EOPNOTSUPP; + return NULL; +} + +#endif /* !defined (PERF_ATTR_SIZE_VER5) */ + /* See linux-btrace.h. */ struct btrace_target_info * @@ -552,6 +857,10 @@ linux_enable_btrace (ptid_t ptid, const struct btrace_config *conf) case BTRACE_FORMAT_BTS: tinfo = linux_enable_bts (ptid, &conf->bts); break; + + case BTRACE_FORMAT_PT: + tinfo = linux_enable_pt (ptid, &conf->pt); + break; } return tinfo; @@ -568,6 +877,18 @@ linux_disable_bts (struct btrace_tinfo_bts *tinfo) return BTRACE_ERR_NONE; } +/* Disable Intel(R) Processor Trace tracing. */ + +static enum btrace_error +linux_disable_pt (struct btrace_tinfo_pt *tinfo) +{ + munmap((void *) tinfo->pt.mem, tinfo->pt.size); + munmap((void *) tinfo->header, PAGE_SIZE); + close (tinfo->file); + + return BTRACE_ERR_NONE; +} + /* See linux-btrace.h. */ enum btrace_error @@ -584,6 +905,10 @@ linux_disable_btrace (struct btrace_target_info *tinfo) case BTRACE_FORMAT_BTS: errcode = linux_disable_bts (&tinfo->variant.bts); break; + + case BTRACE_FORMAT_PT: + errcode = linux_disable_pt (&tinfo->variant.pt); + break; } if (errcode == BTRACE_ERR_NONE) @@ -681,6 +1006,48 @@ linux_read_bts (struct btrace_data_bts *btrace, return BTRACE_ERR_NONE; } +/* Fill in the Intel(R) Processor Trace configuration information. */ + +static void +linux_fill_btrace_pt_config (struct btrace_data_pt_config *conf) +{ + conf->cpu = btrace_this_cpu (); +} + +/* Read branch trace data in Intel(R) Processor Trace format for the thread + given by TINFO into BTRACE using the TYPE reading method. */ + +static enum btrace_error +linux_read_pt (struct btrace_data_pt *btrace, + struct btrace_target_info *tinfo, + enum btrace_read_type type) +{ + struct perf_event_buffer *pt; + + pt = &tinfo->variant.pt.pt; + + linux_fill_btrace_pt_config (&btrace->config); + + switch (type) + { + case BTRACE_READ_DELTA: + /* We don't support delta reads. The data head (i.e. aux_head) wraps + around to stay inside the aux buffer. */ + return BTRACE_ERR_NOT_SUPPORTED; + + case BTRACE_READ_NEW: + if (!perf_event_new_data (pt)) + return BTRACE_ERR_NONE; + + /* Fall through. */ + case BTRACE_READ_ALL: + perf_event_read_all (pt, &btrace->data, &btrace->size); + return BTRACE_ERR_NONE; + } + + internal_error (__FILE__, __LINE__, _("Unkown btrace read type.")); +} + /* See linux-btrace.h. */ enum btrace_error @@ -699,6 +1066,14 @@ linux_read_btrace (struct btrace_data *btrace, btrace->variant.bts.blocks = NULL; return linux_read_bts (&btrace->variant.bts, tinfo, type); + + case BTRACE_FORMAT_PT: + /* We read btrace in Intel(R) Processor Trace format. */ + btrace->format = BTRACE_FORMAT_PT; + btrace->variant.pt.data = NULL; + btrace->variant.pt.size = 0; + + return linux_read_pt (&btrace->variant.pt, tinfo, type); } internal_error (__FILE__, __LINE__, _("Unkown branch trace format.")); diff --git a/gdb/nat/linux-btrace.h b/gdb/nat/linux-btrace.h index bb4a8c5..b680bf5 100644 --- a/gdb/nat/linux-btrace.h +++ b/gdb/nat/linux-btrace.h @@ -62,6 +62,22 @@ struct btrace_tinfo_bts /* The BTS perf event buffer. */ struct perf_event_buffer bts; }; + +/* Branch trace target information for Intel(R) Processor Trace. */ +struct btrace_tinfo_pt +{ + /* The Linux perf_event configuration for collecting the branch trace. */ + struct perf_event_attr attr; + + /* The perf event file. */ + int file; + + /* The perf event configuration page. */ + volatile struct perf_event_mmap_page *header; + + /* The trace perf event buffer. */ + struct perf_event_buffer pt; +}; #endif /* HAVE_LINUX_PERF_EVENT_H */ /* Branch trace target information per thread. */ @@ -79,6 +95,9 @@ struct btrace_target_info { /* CONF.FORMAT == BTRACE_FORMAT_BTS. */ struct btrace_tinfo_bts bts; + + /* CONF.FORMAT == BTRACE_FORMAT_PT. */ + struct btrace_tinfo_pt pt; } variant; #endif /* HAVE_LINUX_PERF_EVENT_H */ |