diff options
Diffstat (limited to 'ptxed')
-rw-r--r-- | ptxed/CMakeLists.txt | 79 | ||||
-rw-r--r-- | ptxed/include/load_elf.h | 64 | ||||
-rw-r--r-- | ptxed/src/load_elf.c | 359 | ||||
-rw-r--r-- | ptxed/src/ptxed.c | 2873 |
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; +} |