summaryrefslogtreecommitdiff
path: root/ptxed
diff options
context:
space:
mode:
Diffstat (limited to 'ptxed')
-rw-r--r--ptxed/CMakeLists.txt79
-rw-r--r--ptxed/include/load_elf.h64
-rw-r--r--ptxed/src/load_elf.c359
-rw-r--r--ptxed/src/ptxed.c2873
4 files changed, 3375 insertions, 0 deletions
diff --git a/ptxed/CMakeLists.txt b/ptxed/CMakeLists.txt
new file mode 100644
index 0000000000000..108dd9b6ef2f6
--- /dev/null
+++ b/ptxed/CMakeLists.txt
@@ -0,0 +1,79 @@
+# Copyright (c) 2013-2019, Intel Corporation
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+set(XED_INCLUDE "" CACHE PATH "")
+set(XED_LIBDIR "" CACHE PATH "")
+
+include_directories(
+ include
+ ../libipt/internal/include
+)
+
+include_directories(SYSTEM
+ ${XED_INCLUDE}
+)
+
+link_directories(
+ ${XED_LIBDIR}
+)
+
+set(PTXED_FILES
+ src/ptxed.c
+ ../libipt/src/pt_cpu.c
+)
+
+if (CMAKE_HOST_UNIX)
+ set(PTXED_FILES ${PTXED_FILES} ../libipt/src/posix/pt_cpuid.c)
+endif (CMAKE_HOST_UNIX)
+
+if (CMAKE_HOST_WIN32)
+ set(PTXED_FILES ${PTXED_FILES} ../libipt/src/windows/pt_cpuid.c)
+endif (CMAKE_HOST_WIN32)
+
+if (FEATURE_ELF)
+ set(PTXED_FILES ${PTXED_FILES} src/load_elf.c)
+endif (FEATURE_ELF)
+
+add_executable(ptxed
+ ${PTXED_FILES}
+)
+target_link_libraries(ptxed libipt)
+target_link_libraries(ptxed xed)
+
+if (SIDEBAND)
+ target_link_libraries(ptxed libipt-sb)
+endif (SIDEBAND)
+
+if (CMAKE_HOST_WIN32)
+ # suppress warnings from XED header files
+ #
+ # w4127: conditional expression is constant
+ # w4244: conversion: possible loss of data
+ #
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4127")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4244")
+
+endif (CMAKE_HOST_WIN32)
diff --git a/ptxed/include/load_elf.h b/ptxed/include/load_elf.h
new file mode 100644
index 0000000000000..4ddc4bb2c5b25
--- /dev/null
+++ b/ptxed/include/load_elf.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2013-2019, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LOAD_ELF_H
+#define LOAD_ELF_H
+
+#include <stdint.h>
+
+struct pt_image_section_cache;
+struct pt_image;
+
+
+/* Load an ELF file.
+ *
+ * Adds sections for all ELF LOAD segments.
+ *
+ * The sections are loaded relative to their virtual addresses specified
+ * in the ELF program header with the lowest address section loaded at @base.
+ *
+ * The name of the program in @prog is used for error reporting.
+ * If @verbose is non-zero, prints information about loaded sections.
+ *
+ * Does not load dependent files.
+ * Does not support dynamic relocations.
+ *
+ * Successfully loaded segments are not unloaded in case of errors.
+ *
+ * If @iscache is not NULL, use it to cache image sections.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ * Returns -pte_invalid if @image or @file are NULL.
+ * Returns -pte_bad_config if @file can't be processed.
+ * Returns -pte_nomem if not enough memory can be allocated.
+ */
+extern int load_elf(struct pt_image_section_cache *iscache,
+ struct pt_image *image, const char *file,
+ uint64_t base, const char *prog, int verbose);
+
+#endif /* LOAD_ELF_H */
diff --git a/ptxed/src/load_elf.c b/ptxed/src/load_elf.c
new file mode 100644
index 0000000000000..46e06d17a114d
--- /dev/null
+++ b/ptxed/src/load_elf.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2013-2019, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "load_elf.h"
+
+#include "intel-pt.h"
+
+#include <stdio.h>
+#include <elf.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+
+
+static int load_section(struct pt_image_section_cache *iscache,
+ struct pt_image *image, const char *name,
+ uint64_t offset, uint64_t size, uint64_t vaddr)
+{
+ if (!iscache)
+ return pt_image_add_file(image, name, offset, size, NULL,
+ vaddr);
+ else {
+ int isid;
+
+ isid = pt_iscache_add_file(iscache, name, offset, size, vaddr);
+ if (isid < 0)
+ return isid;
+
+ return pt_image_add_cached(image, iscache, isid, NULL);
+ }
+}
+
+static int load_elf32(struct pt_image_section_cache *iscache,
+ struct pt_image *image, FILE *file, uint64_t base,
+ const char *name, const char *prog, int verbose)
+{
+ Elf32_Ehdr ehdr;
+ Elf32_Half pidx;
+ uint64_t offset;
+ size_t count;
+ int errcode, sections;
+
+ errcode = fseek(file, 0, SEEK_SET);
+ if (errcode) {
+ fprintf(stderr,
+ "%s: warning: %s error seeking ELF header: %s.\n",
+ prog, name, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ count = fread(&ehdr, sizeof(ehdr), 1, file);
+ if (count != 1) {
+ fprintf(stderr,
+ "%s: warning: %s error reading ELF header: %s.\n",
+ prog, name, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ errcode = fseek(file, (long) ehdr.e_phoff, SEEK_SET);
+ if (errcode) {
+ fprintf(stderr,
+ "%s: warning: %s error seeking program header: %s.\n",
+ prog, name, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ /* Determine the load offset. */
+ if (!base)
+ offset = 0;
+ else {
+ uint64_t minaddr;
+
+ minaddr = UINT64_MAX;
+
+ for (pidx = 0; pidx < ehdr.e_phnum; ++pidx) {
+ Elf32_Phdr phdr;
+
+ count = fread(&phdr, sizeof(phdr), 1, file);
+ if (count != 1) {
+ fprintf(stderr,
+ "%s: warning: %s error reading "
+ "phdr %u: %s.\n",
+ prog, name, pidx, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ if (phdr.p_type != PT_LOAD)
+ continue;
+
+ if (phdr.p_vaddr < minaddr)
+ minaddr = phdr.p_vaddr;
+ }
+
+ offset = base - minaddr;
+ }
+
+ errcode = fseek(file, (long) ehdr.e_phoff, SEEK_SET);
+ if (errcode) {
+ fprintf(stderr,
+ "%s: warning: %s error seeking program header: %s.\n",
+ prog, name, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ for (sections = 0, pidx = 0; pidx < ehdr.e_phnum; ++pidx) {
+ Elf32_Phdr phdr;
+
+ count = fread(&phdr, sizeof(phdr), 1, file);
+ if (count != 1) {
+ fprintf(stderr,
+ "%s: warning: %s error reading phdr %u: %s.\n",
+ prog, name, pidx, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ if (phdr.p_type != PT_LOAD)
+ continue;
+
+ if (!phdr.p_filesz)
+ continue;
+
+ errcode = load_section(iscache, image, name, phdr.p_offset,
+ phdr.p_filesz, phdr.p_vaddr + offset);
+ if (errcode < 0) {
+ fprintf(stderr, "%s: warning: %s: failed to create "
+ "section for phdr %u: %s.\n", prog, name, pidx,
+ pt_errstr(pt_errcode(errcode)));
+ continue;
+ }
+
+ sections += 1;
+
+ if (verbose) {
+ printf("%s:", name);
+ printf(" offset=0x%" PRIx32, phdr.p_offset);
+ printf(" size=0x%" PRIx32, phdr.p_filesz);
+ printf(" vaddr=0x%" PRIx32, phdr.p_vaddr);
+ printf(".\n");
+ }
+ }
+
+ if (!sections)
+ fprintf(stderr,
+ "%s: warning: %s: did not find any load sections.\n",
+ prog, name);
+
+ return 0;
+}
+
+static int load_elf64(struct pt_image_section_cache *iscache,
+ struct pt_image *image, FILE *file, uint64_t base,
+ const char *name, const char *prog, int verbose)
+{
+ Elf64_Ehdr ehdr;
+ Elf64_Half pidx;
+ uint64_t offset;
+ size_t count;
+ int errcode, sections;
+
+ errcode = fseek(file, 0, SEEK_SET);
+ if (errcode) {
+ fprintf(stderr,
+ "%s: warning: %s error seeking ELF header: %s.\n",
+ prog, name, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ count = fread(&ehdr, sizeof(ehdr), 1, file);
+ if (count != 1) {
+ fprintf(stderr,
+ "%s: warning: %s error reading ELF header: %s.\n",
+ prog, name, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ if (LONG_MAX < ehdr.e_phoff) {
+ fprintf(stderr, "%s: warning: %s ELF header too big.\n",
+ prog, name);
+ return -pte_bad_config;
+ }
+
+ errcode = fseek(file, (long) ehdr.e_phoff, SEEK_SET);
+ if (errcode) {
+ fprintf(stderr,
+ "%s: warning: %s error seeking program header: %s.\n",
+ prog, name, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ /* Determine the load offset. */
+ if (!base)
+ offset = 0;
+ else {
+ uint64_t minaddr;
+
+ minaddr = UINT64_MAX;
+
+ for (pidx = 0; pidx < ehdr.e_phnum; ++pidx) {
+ Elf64_Phdr phdr;
+
+ count = fread(&phdr, sizeof(phdr), 1, file);
+ if (count != 1) {
+ fprintf(stderr,
+ "%s: warning: %s error reading "
+ "phdr %u: %s.\n",
+ prog, name, pidx, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ if (phdr.p_type != PT_LOAD)
+ continue;
+
+ if (phdr.p_vaddr < minaddr)
+ minaddr = phdr.p_vaddr;
+ }
+
+ offset = base - minaddr;
+ }
+
+ errcode = fseek(file, (long) ehdr.e_phoff, SEEK_SET);
+ if (errcode) {
+ fprintf(stderr,
+ "%s: warning: %s error seeking program header: %s.\n",
+ prog, name, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ for (sections = 0, pidx = 0; pidx < ehdr.e_phnum; ++pidx) {
+ Elf64_Phdr phdr;
+
+ count = fread(&phdr, sizeof(phdr), 1, file);
+ if (count != 1) {
+ fprintf(stderr,
+ "%s: warning: %s error reading phdr %u: %s.\n",
+ prog, name, pidx, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ if (phdr.p_type != PT_LOAD)
+ continue;
+
+ if (!phdr.p_filesz)
+ continue;
+
+ errcode = load_section(iscache, image, name, phdr.p_offset,
+ phdr.p_filesz, phdr.p_vaddr + offset);
+ if (errcode < 0) {
+ fprintf(stderr, "%s: warning: %s: failed to create "
+ "section for phdr %u: %s.\n", prog, name, pidx,
+ pt_errstr(pt_errcode(errcode)));
+ continue;
+ }
+
+ sections += 1;
+
+ if (verbose) {
+ printf("%s:", name);
+ printf(" offset=0x%" PRIx64, phdr.p_offset);
+ printf(" size=0x%" PRIx64, phdr.p_filesz);
+ printf(" vaddr=0x%" PRIx64, phdr.p_vaddr);
+ printf(".\n");
+ }
+ }
+
+ if (!sections)
+ fprintf(stderr,
+ "%s: warning: %s: did not find any load sections.\n",
+ prog, name);
+
+ return 0;
+}
+
+int load_elf(struct pt_image_section_cache *iscache, struct pt_image *image,
+ const char *name, uint64_t base, const char *prog, int verbose)
+{
+ uint8_t e_ident[EI_NIDENT];
+ FILE *file;
+ size_t count;
+ int errcode, idx;
+
+ if (!image || !name)
+ return -pte_invalid;
+
+ file = fopen(name, "rb");
+ if (!file) {
+ fprintf(stderr, "%s: warning: failed to open %s: %s.\n", prog,
+ name, strerror(errno));
+ return -pte_bad_config;
+ }
+
+ count = fread(e_ident, sizeof(e_ident), 1, file);
+ if (count != 1) {
+ fprintf(stderr,
+ "%s: warning: %s failed to read file header: %s.\n",
+ prog, name, strerror(errno));
+
+ errcode = -pte_bad_config;
+ goto out;
+ }
+
+ for (idx = 0; idx < SELFMAG; ++idx) {
+ if (e_ident[idx] != ELFMAG[idx]) {
+ fprintf(stderr,
+ "%s: warning: ignoring %s: not an ELF file.\n",
+ prog, name);
+
+ errcode = -pte_bad_config;
+ goto out;
+ }
+ }
+
+ switch (e_ident[EI_CLASS]) {
+ default:
+ fprintf(stderr, "%s: unsupported ELF class: %d\n",
+ prog, e_ident[EI_CLASS]);
+ errcode = -pte_bad_config;
+ break;
+
+ case ELFCLASS32:
+ errcode = load_elf32(iscache, image, file, base, name, prog,
+ verbose);
+ break;
+
+ case ELFCLASS64:
+ errcode = load_elf64(iscache, image, file, base, name, prog,
+ verbose);
+ break;
+ }
+
+out:
+ fclose(file);
+ return errcode;
+}
diff --git a/ptxed/src/ptxed.c b/ptxed/src/ptxed.c
new file mode 100644
index 0000000000000..b923d2fb7c000
--- /dev/null
+++ b/ptxed/src/ptxed.c
@@ -0,0 +1,2873 @@
+/*
+ * Copyright (c) 2013-2019, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(FEATURE_ELF)
+# include "load_elf.h"
+#endif /* defined(FEATURE_ELF) */
+
+#include "pt_cpu.h"
+#include "pt_version.h"
+
+#include "intel-pt.h"
+
+#if defined(FEATURE_SIDEBAND)
+# include "libipt-sb.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include <xed-interface.h>
+
+
+/* The type of decoder to be used. */
+enum ptxed_decoder_type {
+ pdt_insn_decoder,
+ pdt_block_decoder
+};
+
+/* The decoder to use. */
+struct ptxed_decoder {
+ /* The decoder type. */
+ enum ptxed_decoder_type type;
+
+ /* The actual decoder. */
+ union {
+ /* If @type == pdt_insn_decoder */
+ struct pt_insn_decoder *insn;
+
+ /* If @type == pdt_block_decoder */
+ struct pt_block_decoder *block;
+ } variant;
+
+ /* Decoder-specific configuration.
+ *
+ * We use a set of structs to store the configuration for multiple
+ * decoders.
+ *
+ * - block decoder.
+ */
+ struct {
+ /* A collection of decoder-specific flags. */
+ struct pt_conf_flags flags;
+ } block;
+
+ /* - instruction flow decoder. */
+ struct {
+ /* A collection of decoder-specific flags. */
+ struct pt_conf_flags flags;
+ } insn;
+
+
+ /* The image section cache. */
+ struct pt_image_section_cache *iscache;
+
+#if defined(FEATURE_SIDEBAND)
+ /* The sideband session. */
+ struct pt_sb_session *session;
+
+#if defined(FEATURE_PEVENT)
+ /* The perf event sideband decoder configuration. */
+ struct pt_sb_pevent_config pevent;
+#endif /* defined(FEATURE_PEVENT) */
+#endif /* defined(FEATURE_SIDEBAND) */
+};
+
+/* A collection of options. */
+struct ptxed_options {
+#if defined(FEATURE_SIDEBAND)
+ /* Sideband dump flags. */
+ uint32_t sb_dump_flags;
+#endif
+ /* Do not print the instruction. */
+ uint32_t dont_print_insn:1;
+
+ /* Remain as quiet as possible - excluding error messages. */
+ uint32_t quiet:1;
+
+ /* Print statistics (overrides quiet). */
+ uint32_t print_stats:1;
+
+ /* Print information about section loads and unloads. */
+ uint32_t track_image:1;
+
+ /* Track blocks in the output.
+ *
+ * This only applies to the block decoder.
+ */
+ uint32_t track_blocks:1;
+
+ /* Print in AT&T format. */
+ uint32_t att_format:1;
+
+ /* Print the offset into the trace file. */
+ uint32_t print_offset:1;
+
+ /* Print the current timestamp. */
+ uint32_t print_time:1;
+
+ /* Print the raw bytes for an insn. */
+ uint32_t print_raw_insn:1;
+
+ /* Perform checks. */
+ uint32_t check:1;
+
+ /* Print the time stamp of events. */
+ uint32_t print_event_time:1;
+
+ /* Print the ip of events. */
+ uint32_t print_event_ip:1;
+
+#if defined(FEATURE_SIDEBAND)
+ /* Print sideband warnings. */
+ uint32_t print_sb_warnings:1;
+#endif
+};
+
+/* A collection of flags selecting which stats to collect/print. */
+enum ptxed_stats_flag {
+ /* Collect number of instructions. */
+ ptxed_stat_insn = (1 << 0),
+
+ /* Collect number of blocks. */
+ ptxed_stat_blocks = (1 << 1)
+};
+
+/* A collection of statistics. */
+struct ptxed_stats {
+ /* The number of instructions. */
+ uint64_t insn;
+
+ /* The number of blocks.
+ *
+ * This only applies to the block decoder.
+ */
+ uint64_t blocks;
+
+ /* A collection of flags saying which statistics to collect/print. */
+ uint32_t flags;
+};
+
+static int ptxed_have_decoder(const struct ptxed_decoder *decoder)
+{
+ /* It suffices to check for one decoder in the variant union. */
+ return decoder && decoder->variant.insn;
+}
+
+static int ptxed_init_decoder(struct ptxed_decoder *decoder)
+{
+ if (!decoder)
+ return -pte_internal;
+
+ memset(decoder, 0, sizeof(*decoder));
+ decoder->type = pdt_block_decoder;
+
+ decoder->iscache = pt_iscache_alloc(NULL);
+ if (!decoder->iscache)
+ return -pte_nomem;
+
+#if defined(FEATURE_SIDEBAND)
+ decoder->session = pt_sb_alloc(decoder->iscache);
+ if (!decoder->session) {
+ pt_iscache_free(decoder->iscache);
+ return -pte_nomem;
+ }
+
+#if defined(FEATURE_PEVENT)
+ memset(&decoder->pevent, 0, sizeof(decoder->pevent));
+ decoder->pevent.size = sizeof(decoder->pevent);
+ decoder->pevent.kernel_start = UINT64_MAX;
+ decoder->pevent.time_mult = 1;
+#endif /* defined(FEATURE_PEVENT) */
+#endif /* defined(FEATURE_SIDEBAND) */
+
+ return 0;
+}
+
+static void ptxed_free_decoder(struct ptxed_decoder *decoder)
+{
+ if (!decoder)
+ return;
+
+ switch (decoder->type) {
+ case pdt_insn_decoder:
+ pt_insn_free_decoder(decoder->variant.insn);
+ break;
+
+ case pdt_block_decoder:
+ pt_blk_free_decoder(decoder->variant.block);
+ break;
+ }
+
+#if defined(FEATURE_SIDEBAND)
+ pt_sb_free(decoder->session);
+#endif
+
+ pt_iscache_free(decoder->iscache);
+}
+
+static void help(const char *name)
+{
+ printf("usage: %s [<options>]\n\n", name);
+ printf("options:\n");
+ printf(" --help|-h this text.\n");
+ printf(" --version display version information and exit.\n");
+ printf(" --att print instructions in att format.\n");
+ printf(" --no-inst do not print instructions (only addresses).\n");
+ printf(" --quiet|-q do not print anything (except errors).\n");
+ printf(" --offset print the offset into the trace file.\n");
+ printf(" --time print the current timestamp.\n");
+ printf(" --raw-insn print the raw bytes of each instruction.\n");
+ printf(" --check perform checks (expensive).\n");
+ printf(" --iscache-limit <size> set the image section cache limit to <size> bytes.\n");
+ printf(" --event:time print the tsc for events if available.\n");
+ printf(" --event:ip print the ip of events if available.\n");
+ printf(" --event:tick request tick events.\n");
+ printf(" --filter:addr<n>_cfg <cfg> set IA32_RTIT_CTL.ADDRn_CFG to <cfg>.\n");
+ printf(" --filter:addr<n>_a <base> set IA32_RTIT_ADDRn_A to <base>.\n");
+ printf(" --filter:addr<n>_b <limit> set IA32_RTIT_ADDRn_B to <limit>.\n");
+ printf(" --stat print statistics (even when quiet).\n");
+ printf(" collects all statistics unless one or more are selected.\n");
+ printf(" --stat:insn collect number of instructions.\n");
+ printf(" --stat:blocks collect number of blocks.\n");
+#if defined(FEATURE_SIDEBAND)
+ printf(" --sb:compact | --sb show sideband records in compact format.\n");
+ printf(" --sb:verbose show sideband records in verbose format.\n");
+ printf(" --sb:filename show the filename on sideband records.\n");
+ printf(" --sb:offset show the offset on sideband records.\n");
+ printf(" --sb:time show the time on sideband records.\n");
+ printf(" --sb:switch print the new image name on context switches.\n");
+ printf(" --sb:warn show sideband warnings.\n");
+#if defined(FEATURE_PEVENT)
+ printf(" --pevent:primary/secondary <file>[:<from>[-<to>]]\n");
+ printf(" load a perf_event sideband stream from <file>.\n");
+ printf(" an optional offset or range can be given.\n");
+ printf(" --pevent:sample-type <val> set perf_event_attr.sample_type to <val> (default: 0).\n");
+ printf(" --pevent:time-zero <val> set perf_event_mmap_page.time_zero to <val> (default: 0).\n");
+ printf(" --pevent:time-shift <val> set perf_event_mmap_page.time_shift to <val> (default: 0).\n");
+ printf(" --pevent:time-mult <val> set perf_event_mmap_page.time_mult to <val> (default: 1).\n");
+ printf(" --pevent:tsc-offset <val> show perf events <val> ticks earlier.\n");
+ printf(" --pevent:kernel-start <val> the start address of the kernel.\n");
+ printf(" --pevent:sysroot <path> prepend <path> to sideband filenames.\n");
+#if defined(FEATURE_ELF)
+ printf(" --pevent:kcore <file> load the kernel from a core dump.\n");
+#endif /* defined(FEATURE_ELF) */
+ printf(" --pevent:vdso-x64 <file> use <file> as 64-bit vdso.\n");
+ printf(" --pevent:vdso-x32 <file> use <file> as x32 vdso.\n");
+ printf(" --pevent:vdso-ia32 <file> use <file> as 32-bit vdso.\n");
+#endif /* defined(FEATURE_PEVENT) */
+#endif /* defined(FEATURE_SIDEBAND) */
+ printf(" --verbose|-v print various information (even when quiet).\n");
+ printf(" --pt <file>[:<from>[-<to>]] load the processor trace data from <file>.\n");
+ printf(" an optional offset or range can be given.\n");
+#if defined(FEATURE_ELF)
+ printf(" --elf <<file>[:<base>] load an ELF from <file> at address <base>.\n");
+ printf(" use the default load address if <base> is omitted.\n");
+#endif /* defined(FEATURE_ELF) */
+ printf(" --raw <file>[:<from>[-<to>]]:<base> load a raw binary from <file> at address <base>.\n");
+ printf(" an optional offset or range can be given.\n");
+ printf(" --cpu none|auto|f/m[/s] set cpu to the given value and decode according to:\n");
+ printf(" none spec (default)\n");
+ printf(" auto current cpu\n");
+ printf(" f/m[/s] family/model[/stepping]\n");
+ printf(" --mtc-freq <n> set the MTC frequency (IA32_RTIT_CTL[17:14]) to <n>.\n");
+ printf(" --nom-freq <n> set the nominal frequency (MSR_PLATFORM_INFO[15:8]) to <n>.\n");
+ printf(" --cpuid-0x15.eax set the value of cpuid[0x15].eax.\n");
+ printf(" --cpuid-0x15.ebx set the value of cpuid[0x15].ebx.\n");
+ printf(" --insn-decoder use the instruction flow decoder (default).\n");
+ printf(" --insn:keep-tcal-on-ovf preserve timing calibration on overflow.\n");
+ printf(" --block-decoder use the block decoder.\n");
+ printf(" --block:show-blocks show blocks in the output.\n");
+ printf(" --block:end-on-call set the end-on-call block decoder flag.\n");
+ printf(" --block:end-on-jump set the end-on-jump block decoder flag.\n");
+ printf(" --block:keep-tcal-on-ovf preserve timing calibration on overflow.\n");
+ printf("\n");
+#if defined(FEATURE_ELF)
+ printf("You must specify at least one binary or ELF file (--raw|--elf).\n");
+#else /* defined(FEATURE_ELF) */
+ printf("You must specify at least one binary file (--raw).\n");
+#endif /* defined(FEATURE_ELF) */
+ printf("You must specify exactly one processor trace file (--pt).\n");
+}
+
+static int extract_base(char *arg, uint64_t *base)
+{
+ char *sep, *rest;
+
+ sep = strrchr(arg, ':');
+ if (sep) {
+ uint64_t num;
+
+ if (!sep[1])
+ return 0;
+
+ errno = 0;
+ num = strtoull(sep+1, &rest, 0);
+ if (errno || *rest)
+ return 0;
+
+ *base = num;
+ *sep = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int parse_range(const char *arg, uint64_t *begin, uint64_t *end)
+{
+ char *rest;
+
+ if (!arg || !*arg)
+ return 0;
+
+ errno = 0;
+ *begin = strtoull(arg, &rest, 0);
+ if (errno)
+ return -1;
+
+ if (!*rest)
+ return 1;
+
+ if (*rest != '-')
+ return -1;
+
+ *end = strtoull(rest+1, &rest, 0);
+ if (errno || *rest)
+ return -1;
+
+ return 2;
+}
+
+/* Preprocess a filename argument.
+ *
+ * A filename may optionally be followed by a file offset or a file range
+ * argument separated by ':'. Split the original argument into the filename
+ * part and the offset/range part.
+ *
+ * If no end address is specified, set @size to zero.
+ * If no offset is specified, set @offset to zero.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int preprocess_filename(char *filename, uint64_t *offset, uint64_t *size)
+{
+ uint64_t begin, end;
+ char *range;
+ int parts;
+
+ if (!filename || !offset || !size)
+ return -pte_internal;
+
+ /* Search from the end as the filename may also contain ':'. */
+ range = strrchr(filename, ':');
+ if (!range) {
+ *offset = 0ull;
+ *size = 0ull;
+
+ return 0;
+ }
+
+ /* Let's try to parse an optional range suffix.
+ *
+ * If we can, remove it from the filename argument.
+ * If we can not, assume that the ':' is part of the filename, e.g. a
+ * drive letter on Windows.
+ */
+ parts = parse_range(range + 1, &begin, &end);
+ if (parts <= 0) {
+ *offset = 0ull;
+ *size = 0ull;
+
+ return 0;
+ }
+
+ if (parts == 1) {
+ *offset = begin;
+ *size = 0ull;
+
+ *range = 0;
+
+ return 0;
+ }
+
+ if (parts == 2) {
+ if (end <= begin)
+ return -pte_invalid;
+
+ *offset = begin;
+ *size = end - begin;
+
+ *range = 0;
+
+ return 0;
+ }
+
+ return -pte_internal;
+}
+
+static int load_file(uint8_t **buffer, size_t *psize, const char *filename,
+ uint64_t offset, uint64_t size, const char *prog)
+{
+ uint8_t *content;
+ size_t read;
+ FILE *file;
+ long fsize, begin, end;
+ int errcode;
+
+ if (!buffer || !psize || !filename || !prog) {
+ fprintf(stderr, "%s: internal error.\n", prog ? prog : "");
+ return -1;
+ }
+
+ errno = 0;
+ file = fopen(filename, "rb");
+ if (!file) {
+ fprintf(stderr, "%s: failed to open %s: %d.\n",
+ prog, filename, errno);
+ return -1;
+ }
+
+ errcode = fseek(file, 0, SEEK_END);
+ if (errcode) {
+ fprintf(stderr, "%s: failed to determine size of %s: %d.\n",
+ prog, filename, errno);
+ goto err_file;
+ }
+
+ fsize = ftell(file);
+ if (fsize < 0) {
+ fprintf(stderr, "%s: failed to determine size of %s: %d.\n",
+ prog, filename, errno);
+ goto err_file;
+ }
+
+ begin = (long) offset;
+ if (((uint64_t) begin != offset) || (fsize <= begin)) {
+ fprintf(stderr,
+ "%s: bad offset 0x%" PRIx64 " into %s.\n",
+ prog, offset, filename);
+ goto err_file;
+ }
+
+ end = fsize;
+ if (size) {
+ uint64_t range_end;
+
+ range_end = offset + size;
+ if ((uint64_t) end < range_end) {
+ fprintf(stderr,
+ "%s: bad range 0x%" PRIx64 " in %s.\n",
+ prog, range_end, filename);
+ goto err_file;
+ }
+
+ end = (long) range_end;
+ }
+
+ fsize = end - begin;
+
+ content = malloc((size_t) fsize);
+ if (!content) {
+ fprintf(stderr, "%s: failed to allocated memory %s.\n",
+ prog, filename);
+ goto err_file;
+ }
+
+ errcode = fseek(file, begin, SEEK_SET);
+ if (errcode) {
+ fprintf(stderr, "%s: failed to load %s: %d.\n",
+ prog, filename, errno);
+ goto err_content;
+ }
+
+ read = fread(content, (size_t) fsize, 1u, file);
+ if (read != 1) {
+ fprintf(stderr, "%s: failed to load %s: %d.\n",
+ prog, filename, errno);
+ goto err_content;
+ }
+
+ fclose(file);
+
+ *buffer = content;
+ *psize = (size_t) fsize;
+
+ return 0;
+
+err_content:
+ free(content);
+
+err_file:
+ fclose(file);
+ return -1;
+}
+
+static int load_pt(struct pt_config *config, char *arg, const char *prog)
+{
+ uint64_t foffset, fsize;
+ uint8_t *buffer;
+ size_t size;
+ int errcode;
+
+ errcode = preprocess_filename(arg, &foffset, &fsize);
+ if (errcode < 0) {
+ fprintf(stderr, "%s: bad file %s: %s.\n", prog, arg,
+ pt_errstr(pt_errcode(errcode)));
+ return -1;
+ }
+
+ errcode = load_file(&buffer, &size, arg, foffset, fsize, prog);
+ if (errcode < 0)
+ return errcode;
+
+ config->begin = buffer;
+ config->end = buffer + size;
+
+ return 0;
+}
+
+static int load_raw(struct pt_image_section_cache *iscache,
+ struct pt_image *image, char *arg, const char *prog)
+{
+ uint64_t base, foffset, fsize;
+ int isid, errcode, has_base;
+
+ has_base = extract_base(arg, &base);
+ if (has_base <= 0) {
+ fprintf(stderr, "%s: failed to parse base address"
+ "from '%s'.\n", prog, arg);
+ return -1;
+ }
+
+ errcode = preprocess_filename(arg, &foffset, &fsize);
+ if (errcode < 0) {
+ fprintf(stderr, "%s: bad file %s: %s.\n", prog, arg,
+ pt_errstr(pt_errcode(errcode)));
+ return -1;
+ }
+
+ if (!fsize)
+ fsize = UINT64_MAX;
+
+ isid = pt_iscache_add_file(iscache, arg, foffset, fsize, base);
+ if (isid < 0) {
+ fprintf(stderr, "%s: failed to add %s at 0x%" PRIx64 ": %s.\n",
+ prog, arg, base, pt_errstr(pt_errcode(isid)));
+ return -1;
+ }
+
+ errcode = pt_image_add_cached(image, iscache, isid, NULL);
+ if (errcode < 0) {
+ fprintf(stderr, "%s: failed to add %s at 0x%" PRIx64 ": %s.\n",
+ prog, arg, base, pt_errstr(pt_errcode(errcode)));
+ return -1;
+ }
+
+ return 0;
+}
+
+static xed_machine_mode_enum_t translate_mode(enum pt_exec_mode mode)
+{
+ switch (mode) {
+ case ptem_unknown:
+ return XED_MACHINE_MODE_INVALID;
+
+ case ptem_16bit:
+ return XED_MACHINE_MODE_LEGACY_16;
+
+ case ptem_32bit:
+ return XED_MACHINE_MODE_LEGACY_32;
+
+ case ptem_64bit:
+ return XED_MACHINE_MODE_LONG_64;
+ }
+
+ return XED_MACHINE_MODE_INVALID;
+}
+
+static const char *visualize_iclass(enum pt_insn_class iclass)
+{
+ switch (iclass) {
+ case ptic_error:
+ return "unknown/error";
+
+ case ptic_other:
+ return "other";
+
+ case ptic_call:
+ return "near call";
+
+ case ptic_return:
+ return "near return";
+
+ case ptic_jump:
+ return "near jump";
+
+ case ptic_cond_jump:
+ return "cond jump";
+
+ case ptic_far_call:
+ return "far call";
+
+ case ptic_far_return:
+ return "far return";
+
+ case ptic_far_jump:
+ return "far jump";
+
+ case ptic_ptwrite:
+ return "ptwrite";
+ }
+
+ return "undefined";
+}
+
+static void check_insn_iclass(const xed_inst_t *inst,
+ const struct pt_insn *insn, uint64_t offset)
+{
+ xed_category_enum_t category;
+ xed_iclass_enum_t iclass;
+
+ if (!inst || !insn) {
+ printf("[internal error]\n");
+ return;
+ }
+
+ category = xed_inst_category(inst);
+ iclass = xed_inst_iclass(inst);
+
+ switch (insn->iclass) {
+ case ptic_error:
+ break;
+
+ case ptic_ptwrite:
+ case ptic_other:
+ switch (category) {
+ default:
+ return;
+
+ case XED_CATEGORY_CALL:
+ case XED_CATEGORY_RET:
+ case XED_CATEGORY_UNCOND_BR:
+ case XED_CATEGORY_SYSCALL:
+ case XED_CATEGORY_SYSRET:
+ break;
+
+ case XED_CATEGORY_COND_BR:
+ switch (iclass) {
+ case XED_ICLASS_XBEGIN:
+ case XED_ICLASS_XEND:
+ return;
+
+ default:
+ break;
+ }
+ break;
+
+ case XED_CATEGORY_INTERRUPT:
+ switch (iclass) {
+ case XED_ICLASS_BOUND:
+ return;
+
+ default:
+ break;
+ }
+ break;
+ }
+ break;
+
+ case ptic_call:
+ if (iclass == XED_ICLASS_CALL_NEAR)
+ return;
+
+ break;
+
+ case ptic_return:
+ if (iclass == XED_ICLASS_RET_NEAR)
+ return;
+
+ break;
+
+ case ptic_jump:
+ if (iclass == XED_ICLASS_JMP)
+ return;
+
+ break;
+
+ case ptic_cond_jump:
+ if (category == XED_CATEGORY_COND_BR)
+ return;
+
+ break;
+
+ case ptic_far_call:
+ switch (iclass) {
+ default:
+ break;
+
+ case XED_ICLASS_CALL_FAR:
+ case XED_ICLASS_INT:
+ case XED_ICLASS_INT1:
+ case XED_ICLASS_INT3:
+ case XED_ICLASS_INTO:
+ case XED_ICLASS_SYSCALL:
+ case XED_ICLASS_SYSCALL_AMD:
+ case XED_ICLASS_SYSENTER:
+ case XED_ICLASS_VMCALL:
+ return;
+ }
+ break;
+
+ case ptic_far_return:
+ switch (iclass) {
+ default:
+ break;
+
+ case XED_ICLASS_RET_FAR:
+ case XED_ICLASS_IRET:
+ case XED_ICLASS_IRETD:
+ case XED_ICLASS_IRETQ:
+ case XED_ICLASS_SYSRET:
+ case XED_ICLASS_SYSRET_AMD:
+ case XED_ICLASS_SYSEXIT:
+ case XED_ICLASS_VMLAUNCH:
+ case XED_ICLASS_VMRESUME:
+ return;
+ }
+ break;
+
+ case ptic_far_jump:
+ if (iclass == XED_ICLASS_JMP_FAR)
+ return;
+
+ break;
+ }
+
+ /* If we get here, @insn->iclass doesn't match XED's classification. */
+ printf("[%" PRIx64 ", %" PRIx64 ": iclass error: iclass: %s, "
+ "xed iclass: %s, category: %s]\n", offset, insn->ip,
+ visualize_iclass(insn->iclass), xed_iclass_enum_t2str(iclass),
+ xed_category_enum_t2str(category));
+
+}
+
+static void check_insn_decode(xed_decoded_inst_t *inst,
+ const struct pt_insn *insn, uint64_t offset)
+{
+ xed_error_enum_t errcode;
+
+ if (!inst || !insn) {
+ printf("[internal error]\n");
+ return;
+ }
+
+ xed_decoded_inst_set_mode(inst, translate_mode(insn->mode),
+ XED_ADDRESS_WIDTH_INVALID);
+
+ /* Decode the instruction (again).
+ *
+ * We may have decoded the instruction already for printing. In this
+ * case, we will decode it twice.
+ *
+ * The more common use-case, however, is to check the instruction class
+ * while not printing instructions since the latter is too expensive for
+ * regular use with long traces.
+ */
+ errcode = xed_decode(inst, insn->raw, insn->size);
+ if (errcode != XED_ERROR_NONE) {
+ printf("[%" PRIx64 ", %" PRIx64 ": xed error: (%u) %s]\n",
+ offset, insn->ip, errcode,
+ xed_error_enum_t2str(errcode));
+ return;
+ }
+
+ if (!xed_decoded_inst_valid(inst)) {
+ printf("[%" PRIx64 ", %" PRIx64 ": xed error: "
+ "invalid instruction]\n", offset, insn->ip);
+ return;
+ }
+}
+
+static void check_insn(const struct pt_insn *insn, uint64_t offset)
+{
+ xed_decoded_inst_t inst;
+
+ if (!insn) {
+ printf("[internal error]\n");
+ return;
+ }
+
+ if (insn->isid <= 0)
+ printf("[%" PRIx64 ", %" PRIx64 ": check error: "
+ "bad isid]\n", offset, insn->ip);
+
+ xed_decoded_inst_zero(&inst);
+ check_insn_decode(&inst, insn, offset);
+
+ /* We need a valid instruction in order to do further checks.
+ *
+ * Invalid instructions have already been diagnosed.
+ */
+ if (!xed_decoded_inst_valid(&inst))
+ return;
+
+ check_insn_iclass(xed_decoded_inst_inst(&inst), insn, offset);
+}
+
+static void print_raw_insn(const struct pt_insn *insn)
+{
+ uint8_t length, idx;
+
+ if (!insn) {
+ printf("[internal error]");
+ return;
+ }
+
+ length = insn->size;
+ if (sizeof(insn->raw) < length)
+ length = sizeof(insn->raw);
+
+ for (idx = 0; idx < length; ++idx)
+ printf(" %02x", insn->raw[idx]);
+
+ for (; idx < pt_max_insn_size; ++idx)
+ printf(" ");
+}
+
+static void xed_print_insn(const xed_decoded_inst_t *inst, uint64_t ip,
+ const struct ptxed_options *options)
+{
+ xed_print_info_t pi;
+ char buffer[256];
+ xed_bool_t ok;
+
+ if (!inst || !options) {
+ printf(" [internal error]");
+ return;
+ }
+
+ if (options->print_raw_insn) {
+ xed_uint_t length, i;
+
+ length = xed_decoded_inst_get_length(inst);
+ for (i = 0; i < length; ++i)
+ printf(" %02x", xed_decoded_inst_get_byte(inst, i));
+
+ for (; i < pt_max_insn_size; ++i)
+ printf(" ");
+ }
+
+ xed_init_print_info(&pi);
+ pi.p = inst;
+ pi.buf = buffer;
+ pi.blen = sizeof(buffer);
+ pi.runtime_address = ip;
+
+ if (options->att_format)
+ pi.syntax = XED_SYNTAX_ATT;
+
+ ok = xed_format_generic(&pi);
+ if (!ok) {
+ printf(" [xed print error]");
+ return;
+ }
+
+ printf(" %s", buffer);
+}
+
+static void print_insn(const struct pt_insn *insn, xed_state_t *xed,
+ const struct ptxed_options *options, uint64_t offset,
+ uint64_t time)
+{
+ if (!insn || !options) {
+ printf("[internal error]\n");
+ return;
+ }
+
+ if (options->print_offset)
+ printf("%016" PRIx64 " ", offset);
+
+ if (options->print_time)
+ printf("%016" PRIx64 " ", time);
+
+ if (insn->speculative)
+ printf("? ");
+
+ printf("%016" PRIx64, insn->ip);
+
+ if (!options->dont_print_insn) {
+ xed_machine_mode_enum_t mode;
+ xed_decoded_inst_t inst;
+ xed_error_enum_t errcode;
+
+ mode = translate_mode(insn->mode);
+
+ xed_state_set_machine_mode(xed, mode);
+ xed_decoded_inst_zero_set_mode(&inst, xed);
+
+ errcode = xed_decode(&inst, insn->raw, insn->size);
+ switch (errcode) {
+ case XED_ERROR_NONE:
+ xed_print_insn(&inst, insn->ip, options);
+ break;
+
+ default:
+ print_raw_insn(insn);
+
+ printf(" [xed decode error: (%u) %s]", errcode,
+ xed_error_enum_t2str(errcode));
+ break;
+ }
+ }
+
+ printf("\n");
+}
+
+static const char *print_exec_mode(enum pt_exec_mode mode)
+{
+ switch (mode) {
+ case ptem_unknown:
+ return "<unknown>";
+
+ case ptem_16bit:
+ return "16-bit";
+
+ case ptem_32bit:
+ return "32-bit";
+
+ case ptem_64bit:
+ return "64-bit";
+ }
+
+ return "<invalid>";
+}
+
+static void print_event(const struct pt_event *event,
+ const struct ptxed_options *options, uint64_t offset)
+{
+ if (!event || !options) {
+ printf("[internal error]\n");
+ return;
+ }
+
+ printf("[");
+
+ if (options->print_offset)
+ printf("%016" PRIx64 " ", offset);
+
+ if (options->print_event_time && event->has_tsc)
+ printf("%016" PRIx64 " ", event->tsc);
+
+ switch (event->type) {
+ case ptev_enabled:
+ printf("%s", event->variant.enabled.resumed ? "resumed" :
+ "enabled");
+
+ if (options->print_event_ip)
+ printf(", ip: %016" PRIx64, event->variant.enabled.ip);
+ break;
+
+ case ptev_disabled:
+ printf("disabled");
+
+ if (options->print_event_ip && !event->ip_suppressed)
+ printf(", ip: %016" PRIx64, event->variant.disabled.ip);
+ break;
+
+ case ptev_async_disabled:
+ printf("disabled");
+
+ if (options->print_event_ip) {
+ printf(", at: %016" PRIx64,
+ event->variant.async_disabled.at);
+
+ if (!event->ip_suppressed)
+ printf(", ip: %016" PRIx64,
+ event->variant.async_disabled.ip);
+ }
+ break;
+
+ case ptev_async_branch:
+ printf("interrupt");
+
+ if (options->print_event_ip) {
+ printf(", from: %016" PRIx64,
+ event->variant.async_branch.from);
+
+ if (!event->ip_suppressed)
+ printf(", to: %016" PRIx64,
+ event->variant.async_branch.to);
+ }
+ break;
+
+ case ptev_paging:
+ printf("paging, cr3: %016" PRIx64 "%s",
+ event->variant.paging.cr3,
+ event->variant.paging.non_root ? ", nr" : "");
+ break;
+
+ case ptev_async_paging:
+ printf("paging, cr3: %016" PRIx64 "%s",
+ event->variant.async_paging.cr3,
+ event->variant.async_paging.non_root ? ", nr" : "");
+
+ if (options->print_event_ip)
+ printf(", ip: %016" PRIx64,
+ event->variant.async_paging.ip);
+ break;
+
+ case ptev_overflow:
+ printf("overflow");
+
+ if (options->print_event_ip && !event->ip_suppressed)
+ printf(", ip: %016" PRIx64, event->variant.overflow.ip);
+ break;
+
+ case ptev_exec_mode:
+ printf("exec mode: %s",
+ print_exec_mode(event->variant.exec_mode.mode));
+
+ if (options->print_event_ip && !event->ip_suppressed)
+ printf(", ip: %016" PRIx64,
+ event->variant.exec_mode.ip);
+ break;
+
+ case ptev_tsx:
+ if (event->variant.tsx.aborted)
+ printf("aborted");
+ else if (event->variant.tsx.speculative)
+ printf("begin transaction");
+ else
+ printf("committed");
+
+ if (options->print_event_ip && !event->ip_suppressed)
+ printf(", ip: %016" PRIx64, event->variant.tsx.ip);
+ break;
+
+ case ptev_stop:
+ printf("stopped");
+ break;
+
+ case ptev_vmcs:
+ printf("vmcs, base: %016" PRIx64, event->variant.vmcs.base);
+ break;
+
+ case ptev_async_vmcs:
+ printf("vmcs, base: %016" PRIx64,
+ event->variant.async_vmcs.base);
+
+ if (options->print_event_ip)
+ printf(", ip: %016" PRIx64,
+ event->variant.async_vmcs.ip);
+ break;
+
+ case ptev_exstop:
+ printf("exstop");
+
+ if (options->print_event_ip && !event->ip_suppressed)
+ printf(", ip: %016" PRIx64, event->variant.exstop.ip);
+ break;
+
+ case ptev_mwait:
+ printf("mwait %" PRIx32 " %" PRIx32,
+ event->variant.mwait.hints, event->variant.mwait.ext);
+
+ if (options->print_event_ip && !event->ip_suppressed)
+ printf(", ip: %016" PRIx64, event->variant.mwait.ip);
+ break;
+
+ case ptev_pwre:
+ printf("pwre c%u.%u", (event->variant.pwre.state + 1) & 0xf,
+ (event->variant.pwre.sub_state + 1) & 0xf);
+
+ if (event->variant.pwre.hw)
+ printf(" hw");
+ break;
+
+
+ case ptev_pwrx:
+ printf("pwrx ");
+
+ if (event->variant.pwrx.interrupt)
+ printf("int: ");
+
+ if (event->variant.pwrx.store)
+ printf("st: ");
+
+ if (event->variant.pwrx.autonomous)
+ printf("hw: ");
+
+ printf("c%u (c%u)", (event->variant.pwrx.last + 1) & 0xf,
+ (event->variant.pwrx.deepest + 1) & 0xf);
+ break;
+
+ case ptev_ptwrite:
+ printf("ptwrite: %" PRIx64, event->variant.ptwrite.payload);
+
+ if (options->print_event_ip && !event->ip_suppressed)
+ printf(", ip: %016" PRIx64, event->variant.ptwrite.ip);
+ break;
+
+ case ptev_tick:
+ printf("tick");
+
+ if (options->print_event_ip && !event->ip_suppressed)
+ printf(", ip: %016" PRIx64, event->variant.tick.ip);
+ break;
+
+ case ptev_cbr:
+ printf("cbr: %x", event->variant.cbr.ratio);
+ break;
+
+ case ptev_mnt:
+ printf("mnt: %" PRIx64, event->variant.mnt.payload);
+ break;
+ }
+
+ printf("]\n");
+}
+
+static void diagnose(struct ptxed_decoder *decoder, uint64_t ip,
+ const char *errtype, int errcode)
+{
+ int err;
+ uint64_t pos;
+
+ err = -pte_internal;
+ pos = 0ull;
+
+ switch (decoder->type) {
+ case pdt_insn_decoder:
+ err = pt_insn_get_offset(decoder->variant.insn, &pos);
+ break;
+
+ case pdt_block_decoder:
+ err = pt_blk_get_offset(decoder->variant.block, &pos);
+ break;
+ }
+
+ if (err < 0) {
+ printf("could not determine offset: %s\n",
+ pt_errstr(pt_errcode(err)));
+ printf("[?, %" PRIx64 ": %s: %s]\n", ip, errtype,
+ pt_errstr(pt_errcode(errcode)));
+ } else
+ printf("[%" PRIx64 ", %" PRIx64 ": %s: %s]\n", pos,
+ ip, errtype, pt_errstr(pt_errcode(errcode)));
+}
+
+#if defined(FEATURE_SIDEBAND)
+
+static int ptxed_sb_event(struct ptxed_decoder *decoder,
+ const struct pt_event *event,
+ const struct ptxed_options *options)
+{
+ struct pt_image *image;
+ int errcode;
+
+ if (!decoder || !event || !options)
+ return -pte_internal;
+
+ image = NULL;
+ errcode = pt_sb_event(decoder->session, &image, event, sizeof(*event),
+ stdout, options->sb_dump_flags);
+ if (errcode < 0)
+ return errcode;
+
+ if (!image)
+ return 0;
+
+ switch (decoder->type) {
+ case pdt_insn_decoder:
+ return pt_insn_set_image(decoder->variant.insn, image);
+
+ case pdt_block_decoder:
+ return pt_blk_set_image(decoder->variant.block, image);
+ }
+
+ return -pte_internal;
+}
+
+#endif /* defined(FEATURE_SIDEBAND) */
+
+static int drain_events_insn(struct ptxed_decoder *decoder, uint64_t *time,
+ int status, const struct ptxed_options *options)
+{
+ struct pt_insn_decoder *ptdec;
+ int errcode;
+
+ if (!decoder || !time || !options)
+ return -pte_internal;
+
+ ptdec = decoder->variant.insn;
+
+ while (status & pts_event_pending) {
+ struct pt_event event;
+ uint64_t offset;
+
+ offset = 0ull;
+ if (options->print_offset) {
+ errcode = pt_insn_get_offset(ptdec, &offset);
+ if (errcode < 0)
+ return errcode;
+ }
+
+ status = pt_insn_event(ptdec, &event, sizeof(event));
+ if (status < 0)
+ return status;
+
+ *time = event.tsc;
+
+ if (!options->quiet && !event.status_update)
+ print_event(&event, options, offset);
+
+#if defined(FEATURE_SIDEBAND)
+ errcode = ptxed_sb_event(decoder, &event, options);
+ if (errcode < 0)
+ return errcode;
+#endif /* defined(FEATURE_SIDEBAND) */
+ }
+
+ return status;
+}
+
+static void decode_insn(struct ptxed_decoder *decoder,
+ const struct ptxed_options *options,
+ struct ptxed_stats *stats)
+{
+ struct pt_insn_decoder *ptdec;
+ xed_state_t xed;
+ uint64_t offset, sync, time;
+
+ if (!decoder || !options) {
+ printf("[internal error]\n");
+ return;
+ }
+
+ xed_state_zero(&xed);
+
+ ptdec = decoder->variant.insn;
+ offset = 0ull;
+ sync = 0ull;
+ time = 0ull;
+ for (;;) {
+ struct pt_insn insn;
+ int status;
+
+ /* Initialize the IP - we use it for error reporting. */
+ insn.ip = 0ull;
+
+ status = pt_insn_sync_forward(ptdec);
+ if (status < 0) {
+ uint64_t new_sync;
+ int errcode;
+
+ if (status == -pte_eos)
+ break;
+
+ diagnose(decoder, insn.ip, "sync error", status);
+
+ /* Let's see if we made any progress. If we haven't,
+ * we likely never will. Bail out.
+ *
+ * We intentionally report the error twice to indicate
+ * that we tried to re-sync. Maybe it even changed.
+ */
+ errcode = pt_insn_get_offset(ptdec, &new_sync);
+ if (errcode < 0 || (new_sync <= sync))
+ break;
+
+ sync = new_sync;
+ continue;
+ }
+
+ for (;;) {
+ status = drain_events_insn(decoder, &time, status,
+ options);
+ if (status < 0)
+ break;
+
+ if (status & pts_eos) {
+ if (!(status & pts_ip_suppressed) &&
+ !options->quiet)
+ printf("[end of trace]\n");
+
+ status = -pte_eos;
+ break;
+ }
+
+ if (options->print_offset || options->check) {
+ int errcode;
+
+ errcode = pt_insn_get_offset(ptdec, &offset);
+ if (errcode < 0)
+ break;
+ }
+
+ status = pt_insn_next(ptdec, &insn, sizeof(insn));
+ if (status < 0) {
+ /* Even in case of errors, we may have succeeded
+ * in decoding the current instruction.
+ */
+ if (insn.iclass != ptic_error) {
+ if (!options->quiet)
+ print_insn(&insn, &xed, options,
+ offset, time);
+ if (stats)
+ stats->insn += 1;
+
+ if (options->check)
+ check_insn(&insn, offset);
+ }
+ break;
+ }
+
+ if (!options->quiet)
+ print_insn(&insn, &xed, options, offset, time);
+
+ if (stats)
+ stats->insn += 1;
+
+ if (options->check)
+ check_insn(&insn, offset);
+ }
+
+ /* We shouldn't break out of the loop without an error. */
+ if (!status)
+ status = -pte_internal;
+
+ /* We're done when we reach the end of the trace stream. */
+ if (status == -pte_eos)
+ break;
+
+ diagnose(decoder, insn.ip, "error", status);
+ }
+}
+
+static int xed_next_ip(uint64_t *pip, const xed_decoded_inst_t *inst,
+ uint64_t ip)
+{
+ xed_uint_t length, disp_width;
+
+ if (!pip || !inst)
+ return -pte_internal;
+
+ length = xed_decoded_inst_get_length(inst);
+ if (!length) {
+ printf("[xed error: failed to determine instruction length]\n");
+ return -pte_bad_insn;
+ }
+
+ ip += length;
+
+ /* If it got a branch displacement it must be a branch.
+ *
+ * This includes conditional branches for which we don't know whether
+ * they were taken. The next IP won't be used in this case as a
+ * conditional branch ends a block. The next block will start with the
+ * correct IP.
+ */
+ disp_width = xed_decoded_inst_get_branch_displacement_width(inst);
+ if (disp_width)
+ ip += (uint64_t) (int64_t)
+ xed_decoded_inst_get_branch_displacement(inst);
+
+ *pip = ip;
+ return 0;
+}
+
+static int block_fetch_insn(struct pt_insn *insn, const struct pt_block *block,
+ uint64_t ip, struct pt_image_section_cache *iscache)
+{
+ if (!insn || !block)
+ return -pte_internal;
+
+ /* We can't read from an empty block. */
+ if (!block->ninsn)
+ return -pte_invalid;
+
+ memset(insn, 0, sizeof(*insn));
+ insn->mode = block->mode;
+ insn->isid = block->isid;
+ insn->ip = ip;
+
+ /* The last instruction in a block may be truncated. */
+ if ((ip == block->end_ip) && block->truncated) {
+ if (!block->size || (sizeof(insn->raw) < (size_t) block->size))
+ return -pte_bad_insn;
+
+ insn->size = block->size;
+ memcpy(insn->raw, block->raw, insn->size);
+ } else {
+ int size;
+
+ size = pt_iscache_read(iscache, insn->raw, sizeof(insn->raw),
+ insn->isid, ip);
+ if (size < 0)
+ return size;
+
+ insn->size = (uint8_t) size;
+ }
+
+ return 0;
+}
+
+static void diagnose_block(struct ptxed_decoder *decoder,
+ const char *errtype, int errcode,
+ const struct pt_block *block)
+{
+ uint64_t ip;
+ int err;
+
+ if (!decoder || !block) {
+ printf("ptxed: internal error");
+ return;
+ }
+
+ /* Determine the IP at which to report the error.
+ *
+ * Depending on the type of error, the IP varies between that of the
+ * last instruction in @block or the next instruction outside of @block.
+ *
+ * When the block is empty, we use the IP of the block itself,
+ * i.e. where the first instruction should have been.
+ */
+ if (!block->ninsn)
+ ip = block->ip;
+ else {
+ ip = block->end_ip;
+
+ switch (errcode) {
+ case -pte_nomap:
+ case -pte_bad_insn: {
+ struct pt_insn insn;
+ xed_decoded_inst_t inst;
+ xed_error_enum_t xederr;
+
+ /* Decode failed when trying to fetch or decode the next
+ * instruction. Since indirect or conditional branches
+ * end a block and don't cause an additional fetch, we
+ * should be able to reach that IP from the last
+ * instruction in @block.
+ *
+ * We ignore errors and fall back to the IP of the last
+ * instruction.
+ */
+ err = block_fetch_insn(&insn, block, ip,
+ decoder->iscache);
+ if (err < 0)
+ break;
+
+ xed_decoded_inst_zero(&inst);
+ xed_decoded_inst_set_mode(&inst,
+ translate_mode(insn.mode),
+ XED_ADDRESS_WIDTH_INVALID);
+
+ xederr = xed_decode(&inst, insn.raw, insn.size);
+ if (xederr != XED_ERROR_NONE)
+ break;
+
+ (void) xed_next_ip(&ip, &inst, insn.ip);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ diagnose(decoder, ip, errtype, errcode);
+}
+
+static void print_block(struct ptxed_decoder *decoder,
+ const struct pt_block *block,
+ const struct ptxed_options *options,
+ const struct ptxed_stats *stats,
+ uint64_t offset, uint64_t time)
+{
+ xed_machine_mode_enum_t mode;
+ xed_state_t xed;
+ uint64_t ip;
+ uint16_t ninsn;
+
+ if (!block || !options) {
+ printf("[internal error]\n");
+ return;
+ }
+
+ if (options->track_blocks) {
+ printf("[block");
+ if (stats)
+ printf(" %" PRIx64, stats->blocks);
+ printf("]\n");
+ }
+
+ mode = translate_mode(block->mode);
+ xed_state_init2(&xed, mode, XED_ADDRESS_WIDTH_INVALID);
+
+ /* There's nothing to do for empty blocks. */
+ ninsn = block->ninsn;
+ if (!ninsn)
+ return;
+
+ ip = block->ip;
+ for (;;) {
+ struct pt_insn insn;
+ xed_decoded_inst_t inst;
+ xed_error_enum_t xederrcode;
+ int errcode;
+
+ if (options->print_offset)
+ printf("%016" PRIx64 " ", offset);
+
+ if (options->print_time)
+ printf("%016" PRIx64 " ", time);
+
+ if (block->speculative)
+ printf("? ");
+
+ printf("%016" PRIx64, ip);
+
+ errcode = block_fetch_insn(&insn, block, ip, decoder->iscache);
+ if (errcode < 0) {
+ printf(" [fetch error: %s]\n",
+ pt_errstr(pt_errcode(errcode)));
+ break;
+ }
+
+ xed_decoded_inst_zero_set_mode(&inst, &xed);
+
+ xederrcode = xed_decode(&inst, insn.raw, insn.size);
+ if (xederrcode != XED_ERROR_NONE) {
+ print_raw_insn(&insn);
+
+ printf(" [xed decode error: (%u) %s]\n", xederrcode,
+ xed_error_enum_t2str(xederrcode));
+ break;
+ }
+
+ if (!options->dont_print_insn)
+ xed_print_insn(&inst, insn.ip, options);
+
+ printf("\n");
+
+ ninsn -= 1;
+ if (!ninsn)
+ break;
+
+ errcode = xed_next_ip(&ip, &inst, ip);
+ if (errcode < 0) {
+ diagnose(decoder, ip, "reconstruct error", errcode);
+ break;
+ }
+ }
+
+ /* Decode should have brought us to @block->end_ip. */
+ if (ip != block->end_ip)
+ diagnose(decoder, ip, "reconstruct error", -pte_nosync);
+}
+
+static void check_block(const struct pt_block *block,
+ struct pt_image_section_cache *iscache,
+ uint64_t offset)
+{
+ struct pt_insn insn;
+ xed_decoded_inst_t inst;
+ uint64_t ip;
+ uint16_t ninsn;
+ int errcode;
+
+ if (!block) {
+ printf("[internal error]\n");
+ return;
+ }
+
+ /* There's nothing to check for an empty block. */
+ ninsn = block->ninsn;
+ if (!ninsn)
+ return;
+
+ if (block->isid <= 0)
+ printf("[%" PRIx64 ", %" PRIx64 ": check error: "
+ "bad isid]\n", offset, block->ip);
+
+ ip = block->ip;
+ do {
+ errcode = block_fetch_insn(&insn, block, ip, iscache);
+ if (errcode < 0) {
+ printf("[%" PRIx64 ", %" PRIx64 ": fetch error: %s]\n",
+ offset, ip, pt_errstr(pt_errcode(errcode)));
+ return;
+ }
+
+ xed_decoded_inst_zero(&inst);
+ check_insn_decode(&inst, &insn, offset);
+
+ /* We need a valid instruction in order to do further checks.
+ *
+ * Invalid instructions have already been diagnosed.
+ */
+ if (!xed_decoded_inst_valid(&inst))
+ return;
+
+ errcode = xed_next_ip(&ip, &inst, ip);
+ if (errcode < 0) {
+ printf("[%" PRIx64 ", %" PRIx64 ": error: %s]\n",
+ offset, ip, pt_errstr(pt_errcode(errcode)));
+ return;
+ }
+ } while (--ninsn);
+
+ /* We reached the end of the block. Both @insn and @inst refer to the
+ * last instruction in @block.
+ *
+ * Check that we reached the end IP of the block.
+ */
+ if (insn.ip != block->end_ip) {
+ printf("[%" PRIx64 ", %" PRIx64 ": error: did not reach end: %"
+ PRIx64 "]\n", offset, insn.ip, block->end_ip);
+ }
+
+ /* Check the last instruction's classification, if available. */
+ insn.iclass = block->iclass;
+ if (insn.iclass)
+ check_insn_iclass(xed_decoded_inst_inst(&inst), &insn, offset);
+}
+
+static int drain_events_block(struct ptxed_decoder *decoder, uint64_t *time,
+ int status, const struct ptxed_options *options)
+{
+ struct pt_block_decoder *ptdec;
+ int errcode;
+
+ if (!decoder || !time || !options)
+ return -pte_internal;
+
+ ptdec = decoder->variant.block;
+
+ while (status & pts_event_pending) {
+ struct pt_event event;
+ uint64_t offset;
+
+ offset = 0ull;
+ if (options->print_offset) {
+ errcode = pt_blk_get_offset(ptdec, &offset);
+ if (errcode < 0)
+ return errcode;
+ }
+
+ status = pt_blk_event(ptdec, &event, sizeof(event));
+ if (status < 0)
+ return status;
+
+ *time = event.tsc;
+
+ if (!options->quiet && !event.status_update)
+ print_event(&event, options, offset);
+
+#if defined(FEATURE_SIDEBAND)
+ errcode = ptxed_sb_event(decoder, &event, options);
+ if (errcode < 0)
+ return errcode;
+#endif /* defined(FEATURE_SIDEBAND) */
+ }
+
+ return status;
+}
+
+static void decode_block(struct ptxed_decoder *decoder,
+ const struct ptxed_options *options,
+ struct ptxed_stats *stats)
+{
+ struct pt_image_section_cache *iscache;
+ struct pt_block_decoder *ptdec;
+ uint64_t offset, sync, time;
+
+ if (!decoder || !options) {
+ printf("[internal error]\n");
+ return;
+ }
+
+ iscache = decoder->iscache;
+ ptdec = decoder->variant.block;
+ offset = 0ull;
+ sync = 0ull;
+ time = 0ull;
+ for (;;) {
+ struct pt_block block;
+ int status;
+
+ /* Initialize IP and ninsn - we use it for error reporting. */
+ block.ip = 0ull;
+ block.ninsn = 0u;
+
+ status = pt_blk_sync_forward(ptdec);
+ if (status < 0) {
+ uint64_t new_sync;
+ int errcode;
+
+ if (status == -pte_eos)
+ break;
+
+ diagnose_block(decoder, "sync error", status, &block);
+
+ /* Let's see if we made any progress. If we haven't,
+ * we likely never will. Bail out.
+ *
+ * We intentionally report the error twice to indicate
+ * that we tried to re-sync. Maybe it even changed.
+ */
+ errcode = pt_blk_get_offset(ptdec, &new_sync);
+ if (errcode < 0 || (new_sync <= sync))
+ break;
+
+ sync = new_sync;
+ continue;
+ }
+
+ for (;;) {
+ status = drain_events_block(decoder, &time, status,
+ options);
+ if (status < 0)
+ break;
+
+ if (status & pts_eos) {
+ if (!(status & pts_ip_suppressed) &&
+ !options->quiet)
+ printf("[end of trace]\n");
+
+ status = -pte_eos;
+ break;
+ }
+
+ if (options->print_offset || options->check) {
+ int errcode;
+
+ errcode = pt_blk_get_offset(ptdec, &offset);
+ if (errcode < 0)
+ break;
+ }
+
+ status = pt_blk_next(ptdec, &block, sizeof(block));
+ if (status < 0) {
+ /* Even in case of errors, we may have succeeded
+ * in decoding some instructions.
+ */
+ if (block.ninsn) {
+ if (stats) {
+ stats->insn += block.ninsn;
+ stats->blocks += 1;
+ }
+
+ if (!options->quiet)
+ print_block(decoder, &block,
+ options, stats,
+ offset, time);
+
+ if (options->check)
+ check_block(&block, iscache,
+ offset);
+ }
+ break;
+ }
+
+ if (stats) {
+ stats->insn += block.ninsn;
+ stats->blocks += 1;
+ }
+
+ if (!options->quiet)
+ print_block(decoder, &block, options, stats,
+ offset, time);
+
+ if (options->check)
+ check_block(&block, iscache, offset);
+ }
+
+ /* We shouldn't break out of the loop without an error. */
+ if (!status)
+ status = -pte_internal;
+
+ /* We're done when we reach the end of the trace stream. */
+ if (status == -pte_eos)
+ break;
+
+ diagnose_block(decoder, "error", status, &block);
+ }
+}
+
+static void decode(struct ptxed_decoder *decoder,
+ const struct ptxed_options *options,
+ struct ptxed_stats *stats)
+{
+ if (!decoder) {
+ printf("[internal error]\n");
+ return;
+ }
+
+ switch (decoder->type) {
+ case pdt_insn_decoder:
+ decode_insn(decoder, options, stats);
+ break;
+
+ case pdt_block_decoder:
+ decode_block(decoder, options, stats);
+ break;
+ }
+}
+
+static int alloc_decoder(struct ptxed_decoder *decoder,
+ const struct pt_config *conf, struct pt_image *image,
+ const struct ptxed_options *options, const char *prog)
+{
+ struct pt_config config;
+ int errcode;
+
+ if (!decoder || !conf || !options || !prog)
+ return -pte_internal;
+
+ config = *conf;
+
+ switch (decoder->type) {
+ case pdt_insn_decoder:
+ config.flags = decoder->insn.flags;
+
+ decoder->variant.insn = pt_insn_alloc_decoder(&config);
+ if (!decoder->variant.insn) {
+ fprintf(stderr,
+ "%s: failed to create decoder.\n", prog);
+ return -pte_nomem;
+ }
+
+ errcode = pt_insn_set_image(decoder->variant.insn, image);
+ if (errcode < 0) {
+ fprintf(stderr, "%s: failed to set image.\n", prog);
+ return errcode;
+ }
+
+ break;
+
+ case pdt_block_decoder:
+ config.flags = decoder->block.flags;
+
+ decoder->variant.block = pt_blk_alloc_decoder(&config);
+ if (!decoder->variant.block) {
+ fprintf(stderr,
+ "%s: failed to create decoder.\n", prog);
+ return -pte_nomem;
+ }
+
+ errcode = pt_blk_set_image(decoder->variant.block, image);
+ if (errcode < 0) {
+ fprintf(stderr, "%s: failed to set image.\n", prog);
+ return errcode;
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+static void print_stats(struct ptxed_stats *stats)
+{
+ if (!stats) {
+ printf("[internal error]\n");
+ return;
+ }
+
+ if (stats->flags & ptxed_stat_insn)
+ printf("insn: %" PRIu64 ".\n", stats->insn);
+
+ if (stats->flags & ptxed_stat_blocks)
+ printf("blocks:\t%" PRIu64 ".\n", stats->blocks);
+}
+
+#if defined(FEATURE_SIDEBAND)
+
+static int ptxed_print_error(int errcode, const char *filename,
+ uint64_t offset, void *priv)
+{
+ const struct ptxed_options *options;
+ const char *errstr, *severity;
+
+ options = (struct ptxed_options *) priv;
+ if (!options)
+ return -pte_internal;
+
+ if (errcode >= 0 && !options->print_sb_warnings)
+ return 0;
+
+ if (!filename)
+ filename = "<unknown>";
+
+ severity = errcode < 0 ? "error" : "warning";
+
+ errstr = errcode < 0
+ ? pt_errstr(pt_errcode(errcode))
+ : pt_sb_errstr((enum pt_sb_error_code) errcode);
+
+ if (!errstr)
+ errstr = "<unknown error>";
+
+ printf("[%s:%016" PRIx64 " sideband %s: %s]\n", filename, offset,
+ severity, errstr);
+
+ return 0;
+}
+
+static int ptxed_print_switch(const struct pt_sb_context *context, void *priv)
+{
+ struct pt_image *image;
+ const char *name;
+
+ if (!priv)
+ return -pte_internal;
+
+ image = pt_sb_ctx_image(context);
+ if (!image)
+ return -pte_internal;
+
+ name = pt_image_name(image);
+ if (!name)
+ name = "<unknown>";
+
+ printf("[context: %s]\n", name);
+
+ return 0;
+}
+
+#if defined(FEATURE_PEVENT)
+
+static int ptxed_sb_pevent(struct ptxed_decoder *decoder, char *filename,
+ const char *prog)
+{
+ struct pt_sb_pevent_config config;
+ uint64_t foffset, fsize, fend;
+ int errcode;
+
+ if (!decoder || !prog) {
+ fprintf(stderr, "%s: internal error.\n", prog ? prog : "?");
+ return -1;
+ }
+
+ errcode = preprocess_filename(filename, &foffset, &fsize);
+ if (errcode < 0) {
+ fprintf(stderr, "%s: bad file %s: %s.\n", prog, filename,
+ pt_errstr(pt_errcode(errcode)));
+ return -1;
+ }
+
+ if (SIZE_MAX < foffset) {
+ fprintf(stderr,
+ "%s: bad offset: 0x%" PRIx64 ".\n", prog, foffset);
+ return -1;
+ }
+
+ config = decoder->pevent;
+ config.filename = filename;
+ config.begin = (size_t) foffset;
+ config.end = 0;
+
+ if (fsize) {
+ fend = foffset + fsize;
+ if ((fend <= foffset) || (SIZE_MAX < fend)) {
+ fprintf(stderr,
+ "%s: bad range: 0x%" PRIx64 "-0x%" PRIx64 ".\n",
+ prog, foffset, fend);
+ return -1;
+ }
+
+ config.end = (size_t) fend;
+ }
+
+ errcode = pt_sb_alloc_pevent_decoder(decoder->session, &config);
+ if (errcode < 0) {
+ fprintf(stderr, "%s: error loading %s: %s.\n", prog, filename,
+ pt_errstr(pt_errcode(errcode)));
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* defined(FEATURE_PEVENT) */
+#endif /* defined(FEATURE_SIDEBAND) */
+
+static int get_arg_uint64(uint64_t *value, const char *option, const char *arg,
+ const char *prog)
+{
+ char *rest;
+
+ if (!value || !option || !prog) {
+ fprintf(stderr, "%s: internal error.\n", prog ? prog : "?");
+ return 0;
+ }
+
+ if (!arg || arg[0] == 0 || (arg[0] == '-' && arg[1] == '-')) {
+ fprintf(stderr, "%s: %s: missing argument.\n", prog, option);
+ return 0;
+ }
+
+ errno = 0;
+ *value = strtoull(arg, &rest, 0);
+ if (errno || *rest) {
+ fprintf(stderr, "%s: %s: bad argument: %s.\n", prog, option,
+ arg);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int get_arg_uint32(uint32_t *value, const char *option, const char *arg,
+ const char *prog)
+{
+ uint64_t val;
+
+ if (!get_arg_uint64(&val, option, arg, prog))
+ return 0;
+
+ if (val > UINT32_MAX) {
+ fprintf(stderr, "%s: %s: value too big: %s.\n", prog, option,
+ arg);
+ return 0;
+ }
+
+ *value = (uint32_t) val;
+
+ return 1;
+}
+
+#if defined(FEATURE_SIDEBAND) && defined(FEATURE_PEVENT)
+
+static int get_arg_uint16(uint16_t *value, const char *option, const char *arg,
+ const char *prog)
+{
+ uint64_t val;
+
+ if (!get_arg_uint64(&val, option, arg, prog))
+ return 0;
+
+ if (val > UINT16_MAX) {
+ fprintf(stderr, "%s: %s: value too big: %s.\n", prog, option,
+ arg);
+ return 0;
+ }
+
+ *value = (uint16_t) val;
+
+ return 1;
+}
+
+#endif /* defined(FEATURE_SIDEBAND) && defined(FEATURE_PEVENT) */
+
+static int get_arg_uint8(uint8_t *value, const char *option, const char *arg,
+ const char *prog)
+{
+ uint64_t val;
+
+ if (!get_arg_uint64(&val, option, arg, prog))
+ return 0;
+
+ if (val > UINT8_MAX) {
+ fprintf(stderr, "%s: %s: value too big: %s.\n", prog, option,
+ arg);
+ return 0;
+ }
+
+ *value = (uint8_t) val;
+
+ return 1;
+}
+
+static int ptxed_addr_cfg(struct pt_config *config, uint8_t filter,
+ const char *option, const char *arg, const char *prog)
+{
+ uint64_t addr_cfg;
+
+ if (!config || !option || !arg || !prog) {
+ fprintf(stderr, "%s: internal error.\n", prog ? prog : "?");
+ return 0;
+ }
+
+ if (!get_arg_uint64(&addr_cfg, option, arg, prog))
+ return 0;
+
+ if (15 < addr_cfg) {
+ fprintf(stderr, "%s: %s: value too big: %s.\n", prog, option,
+ arg);
+ return 0;
+ }
+
+ /* Make sure the shift doesn't overflow. */
+ if (15 < filter) {
+ fprintf(stderr, "%s: internal error.\n", prog);
+ return 0;
+ }
+
+ addr_cfg <<= (filter * 4);
+
+ config->addr_filter.config.addr_cfg |= addr_cfg;
+
+ return 1;
+}
+
+extern int main(int argc, char *argv[])
+{
+ struct ptxed_decoder decoder;
+ struct ptxed_options options;
+ struct ptxed_stats stats;
+ struct pt_config config;
+ struct pt_image *image;
+ const char *prog;
+ int errcode, i;
+
+ if (!argc) {
+ help("");
+ return 1;
+ }
+
+ prog = argv[0];
+ image = NULL;
+
+ memset(&options, 0, sizeof(options));
+ memset(&stats, 0, sizeof(stats));
+
+ pt_config_init(&config);
+
+ errcode = ptxed_init_decoder(&decoder);
+ if (errcode < 0) {
+ fprintf(stderr,
+ "%s: error initializing decoder: %s.\n", prog,
+ pt_errstr(pt_errcode(errcode)));
+ goto err;
+ }
+
+#if defined(FEATURE_SIDEBAND)
+ pt_sb_notify_error(decoder.session, ptxed_print_error, &options);
+#endif
+
+ image = pt_image_alloc(NULL);
+ if (!image) {
+ fprintf(stderr, "%s: failed to allocate image.\n", prog);
+ goto err;
+ }
+
+ for (i = 1; i < argc;) {
+ char *arg;
+
+ arg = argv[i++];
+
+ if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
+ help(prog);
+ goto out;
+ }
+ if (strcmp(arg, "--version") == 0) {
+ pt_print_tool_version(prog);
+ goto out;
+ }
+ if (strcmp(arg, "--pt") == 0) {
+ if (argc <= i) {
+ fprintf(stderr,
+ "%s: --pt: missing argument.\n", prog);
+ goto out;
+ }
+ arg = argv[i++];
+
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: duplicate pt sources: %s.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (config.cpu.vendor) {
+ errcode = pt_cpu_errata(&config.errata,
+ &config.cpu);
+ if (errcode < 0)
+ printf("[0, 0: config error: %s]\n",
+ pt_errstr(pt_errcode(errcode)));
+ }
+
+ errcode = load_pt(&config, arg, prog);
+ if (errcode < 0)
+ goto err;
+
+ errcode = alloc_decoder(&decoder, &config, image,
+ &options, prog);
+ if (errcode < 0)
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--raw") == 0) {
+ if (argc <= i) {
+ fprintf(stderr,
+ "%s: --raw: missing argument.\n", prog);
+ goto out;
+ }
+ arg = argv[i++];
+
+ errcode = load_raw(decoder.iscache, image, arg, prog);
+ if (errcode < 0) {
+ fprintf(stderr, "%s: --raw: failed to load "
+ "'%s'.\n", prog, arg);
+ goto err;
+ }
+
+ continue;
+ }
+#if defined(FEATURE_ELF)
+ if (strcmp(arg, "--elf") == 0) {
+ uint64_t base;
+
+ if (argc <= i) {
+ fprintf(stderr,
+ "%s: --elf: missing argument.\n", prog);
+ goto out;
+ }
+ arg = argv[i++];
+ base = 0ull;
+ errcode = extract_base(arg, &base);
+ if (errcode < 0)
+ goto err;
+
+ errcode = load_elf(decoder.iscache, image, arg, base,
+ prog, options.track_image);
+ if (errcode < 0)
+ goto err;
+
+ continue;
+ }
+#endif /* defined(FEATURE_ELF) */
+ if (strcmp(arg, "--att") == 0) {
+ options.att_format = 1;
+ continue;
+ }
+ if (strcmp(arg, "--no-inst") == 0) {
+ options.dont_print_insn = 1;
+ continue;
+ }
+ if (strcmp(arg, "--quiet") == 0 || strcmp(arg, "-q") == 0) {
+ options.quiet = 1;
+ continue;
+ }
+ if (strcmp(arg, "--offset") == 0) {
+ options.print_offset = 1;
+ continue;
+ }
+ if (strcmp(arg, "--time") == 0) {
+ options.print_time = 1;
+ continue;
+ }
+ if (strcmp(arg, "--raw-insn") == 0) {
+ options.print_raw_insn = 1;
+
+ continue;
+ }
+ if (strcmp(arg, "--event:time") == 0) {
+ options.print_event_time = 1;
+
+ continue;
+ }
+ if (strcmp(arg, "--event:ip") == 0) {
+ options.print_event_ip = 1;
+
+ continue;
+ }
+ if (strcmp(arg, "--event:tick") == 0) {
+ decoder.block.flags.variant.block.
+ enable_tick_events = 1;
+ decoder.insn.flags.variant.insn.enable_tick_events = 1;
+
+ continue;
+ }
+ if (strcmp(arg, "--filter:addr0_cfg") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before --pt.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (!ptxed_addr_cfg(&config, 0, arg, argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--filter:addr0_a") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before --pt.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (!get_arg_uint64(&config.addr_filter.addr0_a, arg,
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--filter:addr0_b") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before --pt.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (!get_arg_uint64(&config.addr_filter.addr0_b, arg,
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--filter:addr1_cfg") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before --pt.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (!ptxed_addr_cfg(&config, 1, arg, argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--filter:addr1_a") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before --pt.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (!get_arg_uint64(&config.addr_filter.addr1_a, arg,
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--filter:addr1_b") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before --pt.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (!get_arg_uint64(&config.addr_filter.addr1_b, arg,
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--filter:addr2_cfg") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before --pt.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (!ptxed_addr_cfg(&config, 2, arg, argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--filter:addr2_a") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before --pt.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (!get_arg_uint64(&config.addr_filter.addr2_a, arg,
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--filter:addr2_b") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before --pt.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (!get_arg_uint64(&config.addr_filter.addr2_b, arg,
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--filter:addr3_cfg") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before --pt.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (!ptxed_addr_cfg(&config, 3, arg, argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--filter:addr3_a") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before --pt.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (!get_arg_uint64(&config.addr_filter.addr3_a, arg,
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--filter:addr3_b") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before --pt.\n",
+ prog, arg);
+ goto err;
+ }
+
+ if (!get_arg_uint64(&config.addr_filter.addr3_b, arg,
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--check") == 0) {
+ options.check = 1;
+ continue;
+ }
+ if (strcmp(arg, "--iscache-limit") == 0) {
+ uint64_t limit;
+
+ if (!get_arg_uint64(&limit, arg, argv[i++], prog))
+ goto err;
+
+ errcode = pt_iscache_set_limit(decoder.iscache, limit);
+ if (errcode < 0) {
+ fprintf(stderr, "%s: error setting iscache "
+ "limit: %s.\n", prog,
+ pt_errstr(pt_errcode(errcode)));
+ goto err;
+ }
+
+ continue;
+ }
+ if (strcmp(arg, "--stat") == 0) {
+ options.print_stats = 1;
+ continue;
+ }
+ if (strcmp(arg, "--stat:insn") == 0) {
+ options.print_stats = 1;
+ stats.flags |= ptxed_stat_insn;
+ continue;
+ }
+ if (strcmp(arg, "--stat:blocks") == 0) {
+ options.print_stats = 1;
+ stats.flags |= ptxed_stat_blocks;
+ continue;
+ }
+#if defined(FEATURE_SIDEBAND)
+ if ((strcmp(arg, "--sb:compact") == 0) ||
+ (strcmp(arg, "--sb") == 0)) {
+ options.sb_dump_flags &= ~(uint32_t) ptsbp_verbose;
+ options.sb_dump_flags |= (uint32_t) ptsbp_compact;
+ continue;
+ }
+ if (strcmp(arg, "--sb:verbose") == 0) {
+ options.sb_dump_flags &= ~(uint32_t) ptsbp_compact;
+ options.sb_dump_flags |= (uint32_t) ptsbp_verbose;
+ continue;
+ }
+ if (strcmp(arg, "--sb:filename") == 0) {
+ options.sb_dump_flags |= (uint32_t) ptsbp_filename;
+ continue;
+ }
+ if (strcmp(arg, "--sb:offset") == 0) {
+ options.sb_dump_flags |= (uint32_t) ptsbp_file_offset;
+ continue;
+ }
+ if (strcmp(arg, "--sb:time") == 0) {
+ options.sb_dump_flags |= (uint32_t) ptsbp_tsc;
+ continue;
+ }
+ if (strcmp(arg, "--sb:switch") == 0) {
+ pt_sb_notify_switch(decoder.session, ptxed_print_switch,
+ &options);
+ continue;
+ }
+ if (strcmp(arg, "--sb:warn") == 0) {
+ options.print_sb_warnings = 1;
+ continue;
+ }
+#if defined(FEATURE_PEVENT)
+ if (strcmp(arg, "--pevent:primary") == 0) {
+ arg = argv[i++];
+ if (!arg) {
+ fprintf(stderr, "%s: --pevent:primary: "
+ "missing argument.\n", prog);
+ goto err;
+ }
+
+ decoder.pevent.primary = 1;
+ errcode = ptxed_sb_pevent(&decoder, arg, prog);
+ if (errcode < 0)
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--pevent:secondary") == 0) {
+ arg = argv[i++];
+ if (!arg) {
+ fprintf(stderr, "%s: --pevent:secondary: "
+ "missing argument.\n", prog);
+ goto err;
+ }
+
+ decoder.pevent.primary = 0;
+ errcode = ptxed_sb_pevent(&decoder, arg, prog);
+ if (errcode < 0)
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--pevent:sample-type") == 0) {
+ if (!get_arg_uint64(&decoder.pevent.sample_type,
+ "--pevent:sample-type",
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--pevent:time-zero") == 0) {
+ if (!get_arg_uint64(&decoder.pevent.time_zero,
+ "--pevent:time-zero",
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--pevent:time-shift") == 0) {
+ if (!get_arg_uint16(&decoder.pevent.time_shift,
+ "--pevent:time-shift",
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--pevent:time-mult") == 0) {
+ if (!get_arg_uint32(&decoder.pevent.time_mult,
+ "--pevent:time-mult",
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--pevent:tsc-offset") == 0) {
+ if (!get_arg_uint64(&decoder.pevent.tsc_offset,
+ "--pevent:tsc-offset",
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--pevent:kernel-start") == 0) {
+ if (!get_arg_uint64(&decoder.pevent.kernel_start,
+ "--pevent:kernel-start",
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--pevent:sysroot") == 0) {
+ arg = argv[i++];
+ if (!arg) {
+ fprintf(stderr, "%s: --pevent:sysroot: "
+ "missing argument.\n", prog);
+ goto err;
+ }
+
+ decoder.pevent.sysroot = arg;
+ continue;
+ }
+#if defined(FEATURE_ELF)
+ if (strcmp(arg, "--pevent:kcore") == 0) {
+ struct pt_image *kernel;
+ uint64_t base;
+
+ arg = argv[i++];
+ if (!arg) {
+ fprintf(stderr, "%s: --pevent:kcore: "
+ "missing argument.\n", prog);
+ goto err;
+ }
+
+ base = 0ull;
+ errcode = extract_base(arg, &base);
+ if (errcode < 0)
+ goto err;
+
+ kernel = pt_sb_kernel_image(decoder.session);
+
+ errcode = load_elf(decoder.iscache, kernel, arg, base,
+ prog, options.track_image);
+ if (errcode < 0)
+ goto err;
+
+ continue;
+ }
+#endif /* defined(FEATURE_ELF) */
+ if (strcmp(arg, "--pevent:vdso-x64") == 0) {
+ arg = argv[i++];
+ if (!arg) {
+ fprintf(stderr, "%s: --pevent:vdso-x64: "
+ "missing argument.\n", prog);
+ goto err;
+ }
+
+ decoder.pevent.vdso_x64 = arg;
+ continue;
+ }
+ if (strcmp(arg, "--pevent:vdso-x32") == 0) {
+ arg = argv[i++];
+ if (!arg) {
+ fprintf(stderr, "%s: --pevent:vdso-x32: "
+ "missing argument.\n", prog);
+ goto err;
+ }
+
+ decoder.pevent.vdso_x32 = arg;
+ continue;
+ }
+ if (strcmp(arg, "--pevent:vdso-ia32") == 0) {
+ arg = argv[i++];
+ if (!arg) {
+ fprintf(stderr, "%s: --pevent:vdso-ia32: "
+ "missing argument.\n", prog);
+ goto err;
+ }
+
+ decoder.pevent.vdso_ia32 = arg;
+ continue;
+ }
+#endif /* defined(FEATURE_PEVENT) */
+#endif /* defined(FEATURE_SIDEBAND) */
+ if (strcmp(arg, "--cpu") == 0) {
+ /* override cpu information before the decoder
+ * is initialized.
+ */
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify cpu before the pt source file.\n",
+ prog);
+ goto err;
+ }
+ if (argc <= i) {
+ fprintf(stderr,
+ "%s: --cpu: missing argument.\n", prog);
+ goto out;
+ }
+ arg = argv[i++];
+
+ if (strcmp(arg, "auto") == 0) {
+ errcode = pt_cpu_read(&config.cpu);
+ if (errcode < 0) {
+ fprintf(stderr,
+ "%s: error reading cpu: %s.\n",
+ prog,
+ pt_errstr(pt_errcode(errcode)));
+ return 1;
+ }
+ continue;
+ }
+
+ if (strcmp(arg, "none") == 0) {
+ memset(&config.cpu, 0, sizeof(config.cpu));
+ continue;
+ }
+
+ errcode = pt_cpu_parse(&config.cpu, arg);
+ if (errcode < 0) {
+ fprintf(stderr,
+ "%s: cpu must be specified as f/m[/s]\n",
+ prog);
+ goto err;
+ }
+ continue;
+ }
+ if (strcmp(arg, "--mtc-freq") == 0) {
+ if (!get_arg_uint8(&config.mtc_freq, "--mtc-freq",
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--nom-freq") == 0) {
+ if (!get_arg_uint8(&config.nom_freq, "--nom-freq",
+ argv[i++], prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--cpuid-0x15.eax") == 0) {
+ if (!get_arg_uint32(&config.cpuid_0x15_eax,
+ "--cpuid-0x15.eax", argv[i++],
+ prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--cpuid-0x15.ebx") == 0) {
+ if (!get_arg_uint32(&config.cpuid_0x15_ebx,
+ "--cpuid-0x15.ebx", argv[i++],
+ prog))
+ goto err;
+
+ continue;
+ }
+ if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0) {
+ options.track_image = 1;
+ continue;
+ }
+
+ if (strcmp(arg, "--insn-decoder") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before the pt "
+ "source file.\n", arg, prog);
+ goto err;
+ }
+
+ decoder.type = pdt_insn_decoder;
+ continue;
+ }
+
+ if (strcmp(arg, "--insn:keep-tcal-on-ovf") == 0) {
+ decoder.insn.flags.variant.insn.keep_tcal_on_ovf = 1;
+ continue;
+ }
+
+
+ if (strcmp(arg, "--block-decoder") == 0) {
+ if (ptxed_have_decoder(&decoder)) {
+ fprintf(stderr,
+ "%s: please specify %s before the pt "
+ "source file.\n", arg, prog);
+ goto err;
+ }
+
+ decoder.type = pdt_block_decoder;
+ continue;
+ }
+
+ if (strcmp(arg, "--block:show-blocks") == 0) {
+ options.track_blocks = 1;
+ continue;
+ }
+
+ if (strcmp(arg, "--block:end-on-call") == 0) {
+ decoder.block.flags.variant.block.end_on_call = 1;
+ continue;
+ }
+
+ if (strcmp(arg, "--block:end-on-jump") == 0) {
+ decoder.block.flags.variant.block.end_on_jump = 1;
+ continue;
+ }
+
+ if (strcmp(arg, "--block:keep-tcal-on-ovf") == 0) {
+ decoder.block.flags.variant.block.keep_tcal_on_ovf = 1;
+ continue;
+ }
+
+ fprintf(stderr, "%s: unknown option: %s.\n", prog, arg);
+ goto err;
+ }
+
+ if (!ptxed_have_decoder(&decoder)) {
+ fprintf(stderr, "%s: no pt file.\n", prog);
+ goto err;
+ }
+
+ xed_tables_init();
+
+ /* If we didn't select any statistics, select them all depending on the
+ * decoder type.
+ */
+ if (options.print_stats && !stats.flags) {
+ stats.flags |= ptxed_stat_insn;
+
+ if (decoder.type == pdt_block_decoder)
+ stats.flags |= ptxed_stat_blocks;
+ }
+
+#if defined(FEATURE_SIDEBAND)
+ errcode = pt_sb_init_decoders(decoder.session);
+ if (errcode < 0) {
+ fprintf(stderr,
+ "%s: error initializing sideband decoders: %s.\n",
+ prog, pt_errstr(pt_errcode(errcode)));
+ goto err;
+ }
+#endif /* defined(FEATURE_SIDEBAND) */
+
+ decode(&decoder, &options, options.print_stats ? &stats : NULL);
+
+ if (options.print_stats)
+ print_stats(&stats);
+
+out:
+ ptxed_free_decoder(&decoder);
+ pt_image_free(image);
+ free(config.begin);
+ return 0;
+
+err:
+ ptxed_free_decoder(&decoder);
+ pt_image_free(image);
+ free(config.begin);
+ return 1;
+}