blob: 72c5bed57c60e559206228cfd47ba51f1437a2f1 [file] [log] [blame]
/*
* Copyright (c) 2013-2017, 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 "pt_cpu.h"
#include "pt_last_ip.h"
#include "pt_time.h"
#include "intel-pt.h"
#include <stdlib.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
struct ptdump_options {
/* Show the current offset in the trace stream. */
uint32_t show_offset:1;
/* Show raw packet bytes. */
uint32_t show_raw_bytes:1;
/* Show last IP for packets with IP payloads. */
uint32_t show_last_ip:1;
/* Show the execution mode on mode.exec. */
uint32_t show_exec_mode:1;
/* Keep track of time. */
uint32_t track_time:1;
/* Show the estimated TSC for timing related packets. */
uint32_t show_time:1;
/* Show time calibration. */
uint32_t show_tcal:1;
/* Show timing information as delta to the previous value. */
uint32_t show_time_as_delta:1;
/* Quiet mode: Don't print anything but errors. */
uint32_t quiet:1;
/* Don't show PAD packets. */
uint32_t no_pad:1;
/* Do not try to sync the decoder. */
uint32_t no_sync:1;
/* Do not calibrate timing. */
uint32_t no_tcal:1;
/* Do not expect wall-clock time. */
uint32_t no_wall_clock:1;
/* Don't show timing packets. */
uint32_t no_timing:1;
/* Don't show CYC packets and ignore them when tracking time. */
uint32_t no_cyc:1;
};
struct ptdump_buffer {
/* The trace offset. */
char offset[17];
/* The raw packet bytes. */
char raw[33];
/* The packet opcode. */
char opcode[10];
union {
/* The standard packet payload. */
char standard[25];
/* An extended packet payload. */
char extended[48];
} payload;
/* The tracking information. */
struct {
/* The tracking identifier. */
char id[5];
/* The tracking information. */
char payload[17];
} tracking;
/* A flag telling whether an extended payload is used. */
uint32_t use_ext_payload:1;
/* A flag telling whether to skip printing this buffer. */
uint32_t skip:1;
/* A flag telling whether to skip printing the time. */
uint32_t skip_time:1;
/* A flag telling whether to skip printing the calibration. */
uint32_t skip_tcal:1;
};
struct ptdump_tracking {
/* Track last-ip. */
struct pt_last_ip last_ip;
/* Track time calibration. */
struct pt_time_cal tcal;
/* Track time. */
struct pt_time time;
/* The last estimated TSC. */
uint64_t tsc;
/* The last calibration value. */
uint64_t fcr;
/* Header vs. normal decode. Set if decoding PSB+. */
uint32_t in_header:1;
};
static int usage(const char *name)
{
fprintf(stderr,
"%s: [<options>] <ptfile>. Use --help or -h for help.\n",
name);
return -1;
}
static int no_file_error(const char *name)
{
fprintf(stderr, "%s: No processor trace file specified.\n", name);
return -1;
}
static int unknown_option_error(const char *arg, const char *name)
{
fprintf(stderr, "%s: unknown option: %s.\n", name, arg);
return -1;
}
static int help(const char *name)
{
printf("usage: %s [<options>] <ptfile>[:<from>[-<to>]\n\n", name);
printf("options:\n");
printf(" --help|-h this text.\n");
printf(" --version display version information and exit.\n");
printf(" --no-sync don't try to sync to the first PSB, assume a valid\n");
printf(" sync point at the beginning of the trace.\n");
printf(" --quiet don't print anything but errors.\n");
printf(" --no-pad don't show PAD packets.\n");
printf(" --no-timing don't show timing packets.\n");
printf(" --no-cyc don't show CYC packets and ignore them when tracking time.\n");
printf(" --no-offset don't show the offset as the first column.\n");
printf(" --raw show raw packet bytes.\n");
printf(" --lastip show last IP updates on packets with IP payloads.\n");
printf(" --exec-mode show the current execution mode on mode.exec packets.\n");
printf(" --time show the estimated TSC on timing packets.\n");
printf(" --tcal show time calibration information.\n");
printf(" --time-delta show timing information as delta.\n");
printf(" --no-tcal skip timing calibration.\n");
printf(" this will result in errors when CYC packets are encountered.\n");
printf(" --no-wall-clock suppress the no-time error and print relative time.\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(" <ptfile>[:<from>[-<to>]] load the processor trace data from <ptfile>;\n");
return 0;
}
static int version(const char *name)
{
struct pt_version v = pt_library_version();
printf("%s-%d.%d.%d%s / libipt-%" PRIu8 ".%" PRIu8 ".%" PRIu32 "%s\n",
name, PT_VERSION_MAJOR, PT_VERSION_MINOR, PT_VERSION_BUILD,
PT_VERSION_EXT, v.major, v.minor, v.build, v.ext);
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(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, fsize, 1, file);
if (read != 1) {
fprintf(stderr, "%s: failed to load %s: %d.\n",
prog, filename, errno);
goto err_content;
}
fclose(file);
*buffer = content;
*psize = fsize;
return 0;
err_content:
free(content);
err_file:
fclose(file);
return -1;
}
static int load_pt(struct pt_config *config, const char *filename,
uint64_t foffset, uint64_t fsize, const char *prog)
{
uint8_t *buffer;
size_t size;
int errcode;
errcode = load_file(&buffer, &size, filename, foffset, fsize, prog);
if (errcode < 0)
return errcode;
config->begin = buffer;
config->end = buffer + size;
return 0;
}
static int diag(const char *errstr, uint64_t offset, int errcode)
{
if (errcode)
printf("[%" PRIx64 ": %s: %s]\n", offset, errstr,
pt_errstr(pt_errcode(errcode)));
else
printf("[%" PRIx64 ": %s]\n", offset, errstr);
return errcode;
}
static void ptdump_tracking_init(struct ptdump_tracking *tracking)
{
if (!tracking)
return;
pt_last_ip_init(&tracking->last_ip);
pt_tcal_init(&tracking->tcal);
pt_time_init(&tracking->time);
tracking->tsc = 0ull;
tracking->fcr = 0ull;
tracking->in_header = 0;
}
static void ptdump_tracking_reset(struct ptdump_tracking *tracking)
{
if (!tracking)
return;
pt_last_ip_init(&tracking->last_ip);
pt_tcal_init(&tracking->tcal);
pt_time_init(&tracking->time);
tracking->tsc = 0ull;
tracking->fcr = 0ull;
tracking->in_header = 0;
}
static void ptdump_tracking_fini(struct ptdump_tracking *tracking)
{
(void) tracking;
/* Nothing to do. */
}
#define print_field(field, ...) \
do { \
/* Avoid partial overwrites. */ \
memset(field, 0, sizeof(field)); \
snprintf(field, sizeof(field), __VA_ARGS__); \
} while (0)
static int print_buffer(struct ptdump_buffer *buffer, uint64_t offset,
const struct ptdump_options *options)
{
const char *sep;
if (!buffer)
return diag("error printing buffer", offset, -pte_internal);
if (buffer->skip || options->quiet)
return 0;
/* Make sure the first column starts at the beginning of the line - no
* matter what column is first.
*/
sep = "";
if (options->show_offset) {
printf("%-*s", (int) sizeof(buffer->offset), buffer->offset);
sep = " ";
}
if (buffer->raw[0]) {
printf("%s%-*s", sep, (int) sizeof(buffer->raw), buffer->raw);
sep = " ";
}
if (buffer->payload.standard[0])
printf("%s%-*s", sep, (int) sizeof(buffer->opcode),
buffer->opcode);
else
printf("%s%s", sep, buffer->opcode);
/* We printed at least one column. From this point on, we don't need
* the separator any longer.
*/
if (buffer->use_ext_payload)
printf(" %s", buffer->payload.extended);
else if (buffer->tracking.id[0]) {
printf(" %-*s", (int) sizeof(buffer->payload.standard),
buffer->payload.standard);
printf(" %-*s", (int) sizeof(buffer->tracking.id),
buffer->tracking.id);
printf("%s", buffer->tracking.payload);
} else if (buffer->payload.standard[0])
printf(" %s", buffer->payload.standard);
printf("\n");
return 0;
}
static int print_raw(struct ptdump_buffer *buffer, uint64_t offset,
const struct pt_packet *packet,
const struct pt_config *config)
{
const uint8_t *begin, *end;
char *bbegin, *bend;
if (!buffer || !packet)
return diag("error printing packet", offset, -pte_internal);
begin = config->begin + offset;
end = begin + packet->size;
if (config->end < end)
return diag("bad packet size", offset, -pte_bad_packet);
bbegin = buffer->raw;
bend = bbegin + sizeof(buffer->raw);
for (; begin < end; ++begin) {
char *pos;
pos = bbegin;
bbegin += 2;
if (bend <= bbegin)
return diag("truncating raw packet", offset, 0);
sprintf(pos, "%02x", *begin);
}
return 0;
}
static int track_last_ip(struct ptdump_buffer *buffer,
struct pt_last_ip *last_ip, uint64_t offset,
const struct pt_packet_ip *packet,
const struct ptdump_options *options,
const struct pt_config *config)
{
uint64_t ip;
int errcode;
if (!buffer || !options)
return diag("error tracking last-ip", offset, -pte_internal);
print_field(buffer->tracking.id, "ip");
errcode = pt_last_ip_update_ip(last_ip, packet, config);
if (errcode < 0) {
print_field(buffer->tracking.payload, "<unavailable>");
return diag("error tracking last-ip", offset, errcode);
}
errcode = pt_last_ip_query(&ip, last_ip);
if (errcode < 0) {
if (errcode == -pte_ip_suppressed)
print_field(buffer->tracking.payload, "<suppressed>");
else {
print_field(buffer->tracking.payload, "<unavailable>");
return diag("error tracking last-ip", offset, errcode);
}
} else
print_field(buffer->tracking.payload, "%016" PRIx64, ip);
return 0;
}
static int print_time(struct ptdump_buffer *buffer,
struct ptdump_tracking *tracking, uint64_t offset,
const struct ptdump_options *options)
{
uint64_t tsc;
int errcode;
if (!tracking || !options)
return diag("error printing time", offset, -pte_internal);
print_field(buffer->tracking.id, "tsc");
errcode = pt_time_query_tsc(&tsc, NULL, NULL, &tracking->time);
if (errcode < 0) {
switch (-errcode) {
case pte_no_time:
if (options->no_wall_clock)
break;
/* Fall through. */
default:
diag("error printing time", offset, errcode);
print_field(buffer->tracking.payload, "<unavailable>");
return errcode;
}
}
if (options->show_time_as_delta) {
uint64_t old_tsc;
old_tsc = tracking->tsc;
if (old_tsc <= tsc)
print_field(buffer->tracking.payload, "+%" PRIx64,
tsc - old_tsc);
else
print_field(buffer->tracking.payload, "-%" PRIx64,
old_tsc - tsc);
tracking->tsc = tsc;
} else
print_field(buffer->tracking.payload, "%016" PRIx64, tsc);
return 0;
}
static int print_tcal(struct ptdump_buffer *buffer,
struct ptdump_tracking *tracking, uint64_t offset,
const struct ptdump_options *options)
{
uint64_t fcr;
double dfcr;
int errcode;
if (!tracking || !options)
return diag("error printing time", offset, -pte_internal);
print_field(buffer->tracking.id, "fcr");
errcode = pt_tcal_fcr(&fcr, &tracking->tcal);
if (errcode < 0) {
print_field(buffer->tracking.payload, "<unavailable>");
return diag("error printing time", offset, errcode);
}
/* We print fcr as double to account for the shift. */
dfcr = (double) fcr;
dfcr /= (double) (1ull << pt_tcal_fcr_shr);
if (options->show_time_as_delta) {
uint64_t old_fcr;
double dold_fcr;
old_fcr = tracking->fcr;
/* We print fcr as double to account for the shift. */
dold_fcr = (double) old_fcr;
dold_fcr /= (double) (1ull << pt_tcal_fcr_shr);
if (old_fcr <= fcr)
print_field(buffer->tracking.payload, "+%.3f",
dfcr - dold_fcr);
else
print_field(buffer->tracking.payload, "-%.3f",
dold_fcr - dfcr);
tracking->fcr = fcr;
} else
print_field(buffer->tracking.payload, "%.3f", dfcr);
return 0;
}
static int track_time(struct ptdump_buffer *buffer,
struct ptdump_tracking *tracking, uint64_t offset,
const struct ptdump_options *options)
{
if (!tracking || !options)
return diag("error tracking time", offset, -pte_internal);
if (options->show_tcal && !buffer->skip_tcal)
print_tcal(buffer, tracking, offset, options);
if (options->show_time && !buffer->skip_time)
print_time(buffer, tracking, offset, options);
return 0;
}
static int track_tsc(struct ptdump_buffer *buffer,
struct ptdump_tracking *tracking, uint64_t offset,
const struct pt_packet_tsc *packet,
const struct ptdump_options *options,
const struct pt_config *config)
{
int errcode;
if (!buffer || !tracking || !options)
return diag("error tracking time", offset, -pte_internal);
if (!options->no_tcal) {
errcode = tracking->in_header ?
pt_tcal_header_tsc(&tracking->tcal, packet, config) :
pt_tcal_update_tsc(&tracking->tcal, packet, config);
if (errcode < 0)
diag("error calibrating time", offset, errcode);
}
errcode = pt_time_update_tsc(&tracking->time, packet, config);
if (errcode < 0)
diag("error updating time", offset, errcode);
return track_time(buffer, tracking, offset, options);
}
static int track_cbr(struct ptdump_buffer *buffer,
struct ptdump_tracking *tracking, uint64_t offset,
const struct pt_packet_cbr *packet,
const struct ptdump_options *options,
const struct pt_config *config)
{
int errcode;
if (!buffer || !tracking || !options)
return diag("error tracking time", offset, -pte_internal);
if (!options->no_tcal) {
errcode = tracking->in_header ?
pt_tcal_header_cbr(&tracking->tcal, packet, config) :
pt_tcal_update_cbr(&tracking->tcal, packet, config);
if (errcode < 0)
diag("error calibrating time", offset, errcode);
}
errcode = pt_time_update_cbr(&tracking->time, packet, config);
if (errcode < 0)
diag("error updating time", offset, errcode);
/* There is no timing update at this packet. */
buffer->skip_time = 1;
return track_time(buffer, tracking, offset, options);
}
static int track_tma(struct ptdump_buffer *buffer,
struct ptdump_tracking *tracking, uint64_t offset,
const struct pt_packet_tma *packet,
const struct ptdump_options *options,
const struct pt_config *config)
{
int errcode;
if (!buffer || !tracking || !options)
return diag("error tracking time", offset, -pte_internal);
if (!options->no_tcal) {
errcode = pt_tcal_update_tma(&tracking->tcal, packet, config);
if (errcode < 0)
diag("error calibrating time", offset, errcode);
}
errcode = pt_time_update_tma(&tracking->time, packet, config);
if (errcode < 0)
diag("error updating time", offset, errcode);
/* There is no calibration update at this packet. */
buffer->skip_tcal = 1;
return track_time(buffer, tracking, offset, options);
}
static int track_mtc(struct ptdump_buffer *buffer,
struct ptdump_tracking *tracking, uint64_t offset,
const struct pt_packet_mtc *packet,
const struct ptdump_options *options,
const struct pt_config *config)
{
int errcode;
if (!buffer || !tracking || !options)
return diag("error tracking time", offset, -pte_internal);
if (!options->no_tcal) {
errcode = pt_tcal_update_mtc(&tracking->tcal, packet, config);
if (errcode < 0)
diag("error calibrating time", offset, errcode);
}
errcode = pt_time_update_mtc(&tracking->time, packet, config);
if (errcode < 0)
diag("error updating time", offset, errcode);
return track_time(buffer, tracking, offset, options);
}
static int track_cyc(struct ptdump_buffer *buffer,
struct ptdump_tracking *tracking, uint64_t offset,
const struct pt_packet_cyc *packet,
const struct ptdump_options *options,
const struct pt_config *config)
{
uint64_t fcr;
int errcode;
if (!buffer || !tracking || !options)
return diag("error tracking time", offset, -pte_internal);
/* Initialize to zero in case of calibration errors. */
fcr = 0ull;
if (!options->no_tcal) {
errcode = pt_tcal_fcr(&fcr, &tracking->tcal);
if (errcode < 0)
diag("calibration error", offset, errcode);
errcode = pt_tcal_update_cyc(&tracking->tcal, packet, config);
if (errcode < 0)
diag("error calibrating time", offset, errcode);
}
errcode = pt_time_update_cyc(&tracking->time, packet, config, fcr);
if (errcode < 0)
diag("error updating time", offset, errcode);
else if (!fcr)
diag("error updating time: no calibration", offset, 0);
/* There is no calibration update at this packet. */
buffer->skip_tcal = 1;
return track_time(buffer, tracking, offset, options);
}
static uint64_t sext(uint64_t val, uint8_t sign)
{
uint64_t signbit, mask;
signbit = 1ull << (sign - 1);
mask = ~0ull << sign;
return val & signbit ? val | mask : val & ~mask;
}
static int print_ip_payload(struct ptdump_buffer *buffer, uint64_t offset,
const struct pt_packet_ip *packet)
{
if (!buffer || !packet)
return diag("error printing payload", offset, -pte_internal);
switch (packet->ipc) {
case pt_ipc_suppressed:
print_field(buffer->payload.standard, "%x: ????????????????",
pt_ipc_suppressed);
return 0;
case pt_ipc_update_16:
print_field(buffer->payload.standard, "%x: ????????????%04"
PRIx64, pt_ipc_update_16, packet->ip);
return 0;
case pt_ipc_update_32:
print_field(buffer->payload.standard, "%x: ????????%08"
PRIx64, pt_ipc_update_32, packet->ip);
return 0;
case pt_ipc_update_48:
print_field(buffer->payload.standard, "%x: ????%012"
PRIx64, pt_ipc_update_48, packet->ip);
return 0;
case pt_ipc_sext_48:
print_field(buffer->payload.standard, "%x: %016" PRIx64,
pt_ipc_sext_48, sext(packet->ip, 48));
return 0;
case pt_ipc_full:
print_field(buffer->payload.standard, "%x: %016" PRIx64,
pt_ipc_full, packet->ip);
return 0;
}
print_field(buffer->payload.standard, "%x: %016" PRIx64,
packet->ipc, packet->ip);
return diag("bad ipc", offset, -pte_bad_packet);
}
static int print_tnt_payload(struct ptdump_buffer *buffer, uint64_t offset,
const struct pt_packet_tnt *packet)
{
uint64_t tnt;
uint8_t bits;
char *begin, *end;
if (!buffer || !packet)
return diag("error printing payload", offset, -pte_internal);
bits = packet->bit_size;
tnt = packet->payload;
begin = buffer->payload.extended;
end = begin + bits;
if (sizeof(buffer->payload.extended) < bits) {
diag("truncating tnt payload", offset, 0);
end = begin + sizeof(buffer->payload.extended);
}
for (; begin < end; ++begin, --bits)
*begin = tnt & (1ull << (bits - 1)) ? '!' : '.';
return 0;
}
static const char *print_exec_mode(const struct pt_packet_mode_exec *packet,
uint64_t offset)
{
enum pt_exec_mode mode;
mode = pt_get_exec_mode(packet);
switch (mode) {
case ptem_64bit:
return "64-bit";
case ptem_32bit:
return "32-bit";
case ptem_16bit:
return "16-bit";
case ptem_unknown:
return "unknown";
}
diag("bad exec mode", offset, -pte_bad_packet);
return "invalid";
}
static int print_packet(struct ptdump_buffer *buffer, uint64_t offset,
const struct pt_packet *packet,
struct ptdump_tracking *tracking,
const struct ptdump_options *options,
const struct pt_config *config)
{
if (!buffer || !packet || !tracking || !options)
return diag("error printing packet", offset, -pte_internal);
switch (packet->type) {
case ppt_unknown:
print_field(buffer->opcode, "<unknown>");
return 0;
case ppt_invalid:
print_field(buffer->opcode, "<invalid>");
return 0;
case ppt_psb:
print_field(buffer->opcode, "psb");
tracking->in_header = 1;
return 0;
case ppt_psbend:
print_field(buffer->opcode, "psbend");
tracking->in_header = 0;
return 0;
case ppt_pad:
print_field(buffer->opcode, "pad");
if (options->no_pad)
buffer->skip = 1;
return 0;
case ppt_ovf:
print_field(buffer->opcode, "ovf");
return 0;
case ppt_stop:
print_field(buffer->opcode, "stop");
return 0;
case ppt_fup:
print_field(buffer->opcode, "fup");
print_ip_payload(buffer, offset, &packet->payload.ip);
if (options->show_last_ip)
track_last_ip(buffer, &tracking->last_ip, offset,
&packet->payload.ip, options, config);
return 0;
case ppt_tip:
print_field(buffer->opcode, "tip");
print_ip_payload(buffer, offset, &packet->payload.ip);
if (options->show_last_ip)
track_last_ip(buffer, &tracking->last_ip, offset,
&packet->payload.ip, options, config);
return 0;
case ppt_tip_pge:
print_field(buffer->opcode, "tip.pge");
print_ip_payload(buffer, offset, &packet->payload.ip);
if (options->show_last_ip)
track_last_ip(buffer, &tracking->last_ip, offset,
&packet->payload.ip, options, config);
return 0;
case ppt_tip_pgd:
print_field(buffer->opcode, "tip.pgd");
print_ip_payload(buffer, offset, &packet->payload.ip);
if (options->show_last_ip)
track_last_ip(buffer, &tracking->last_ip, offset,
&packet->payload.ip, options, config);
return 0;
case ppt_pip:
print_field(buffer->opcode, "pip");
print_field(buffer->payload.standard, "%" PRIx64 "%s",
packet->payload.pip.cr3,
packet->payload.pip.nr ? ", nr" : "");
print_field(buffer->tracking.id, "cr3");
print_field(buffer->tracking.payload, "%016" PRIx64,
packet->payload.pip.cr3);
return 0;
case ppt_vmcs:
print_field(buffer->opcode, "vmcs");
print_field(buffer->payload.standard, "%" PRIx64,
packet->payload.vmcs.base);
print_field(buffer->tracking.id, "vmcs");
print_field(buffer->tracking.payload, "%016" PRIx64,
packet->payload.vmcs.base);
return 0;
case ppt_tnt_8:
print_field(buffer->opcode, "tnt.8");
return print_tnt_payload(buffer, offset, &packet->payload.tnt);
case ppt_tnt_64:
print_field(buffer->opcode, "tnt.64");
return print_tnt_payload(buffer, offset, &packet->payload.tnt);
case ppt_mode: {
const struct pt_packet_mode *mode;
mode = &packet->payload.mode;
switch (mode->leaf) {
case pt_mol_exec: {
const char *csd, *csl, *sep;
csd = mode->bits.exec.csd ? "cs.d" : "";
csl = mode->bits.exec.csl ? "cs.l" : "";
sep = csd[0] && csl[0] ? ", " : "";
print_field(buffer->opcode, "mode.exec");
print_field(buffer->payload.standard, "%s%s%s",
csd, sep, csl);
if (options->show_exec_mode) {
const char *em;
em = print_exec_mode(&mode->bits.exec, offset);
print_field(buffer->tracking.id, "em");
print_field(buffer->tracking.payload, "%s", em);
}
}
return 0;
case pt_mol_tsx: {
const char *intx, *abrt, *sep;
intx = mode->bits.tsx.intx ? "intx" : "";
abrt = mode->bits.tsx.abrt ? "abrt" : "";
sep = intx[0] && abrt[0] ? ", " : "";
print_field(buffer->opcode, "mode.tsx");
print_field(buffer->payload.standard, "%s%s%s",
intx, sep, abrt);
}
return 0;
}
print_field(buffer->opcode, "mode");
print_field(buffer->payload.standard, "leaf: %x", mode->leaf);
return diag("unknown mode leaf", offset, 0);
}
case ppt_tsc:
print_field(buffer->opcode, "tsc");
print_field(buffer->payload.standard, "%" PRIx64,
packet->payload.tsc.tsc);
if (options->track_time)
track_tsc(buffer, tracking, offset,
&packet->payload.tsc, options, config);
if (options->no_timing)
buffer->skip = 1;
return 0;
case ppt_cbr:
print_field(buffer->opcode, "cbr");
print_field(buffer->payload.standard, "%x",
packet->payload.cbr.ratio);
if (options->track_time)
track_cbr(buffer, tracking, offset,
&packet->payload.cbr, options, config);
if (options->no_timing)
buffer->skip = 1;
return 0;
case ppt_tma:
print_field(buffer->opcode, "tma");
print_field(buffer->payload.standard, "%x, %x",
packet->payload.tma.ctc, packet->payload.tma.fc);
if (options->track_time)
track_tma(buffer, tracking, offset,
&packet->payload.tma, options, config);
if (options->no_timing)
buffer->skip = 1;
return 0;
case ppt_mtc:
print_field(buffer->opcode, "mtc");
print_field(buffer->payload.standard, "%x",
packet->payload.mtc.ctc);
if (options->track_time)
track_mtc(buffer, tracking, offset,
&packet->payload.mtc, options, config);
if (options->no_timing)
buffer->skip = 1;
return 0;
case ppt_cyc:
print_field(buffer->opcode, "cyc");
print_field(buffer->payload.standard, "%" PRIx64,
packet->payload.cyc.value);
if (options->track_time && !options->no_cyc)
track_cyc(buffer, tracking, offset,
&packet->payload.cyc, options, config);
if (options->no_timing || options->no_cyc)
buffer->skip = 1;
return 0;
case ppt_mnt:
print_field(buffer->opcode, "mnt");
print_field(buffer->payload.standard, "%" PRIx64,
packet->payload.mnt.payload);
return 0;
}
return diag("unknown packet", offset, -pte_bad_opc);
}
static int dump_one_packet(uint64_t offset, const struct pt_packet *packet,
struct ptdump_tracking *tracking,
const struct ptdump_options *options,
const struct pt_config *config)
{
struct ptdump_buffer buffer;
int errcode;
memset(&buffer, 0, sizeof(buffer));
print_field(buffer.offset, "%016" PRIx64, offset);
if (options->show_raw_bytes) {
errcode = print_raw(&buffer, offset, packet, config);
if (errcode < 0)
return errcode;
}
errcode = print_packet(&buffer, offset, packet, tracking, options,
config);
if (errcode < 0)
return errcode;
return print_buffer(&buffer, offset, options);
}
static int dump_packets(struct pt_packet_decoder *decoder,
struct ptdump_tracking *tracking,
const struct ptdump_options *options,
const struct pt_config *config)
{
uint64_t offset;
int errcode;
offset = 0ull;
for (;;) {
struct pt_packet packet;
errcode = pt_pkt_get_offset(decoder, &offset);
if (errcode < 0)
return diag("error getting offset", offset, errcode);
errcode = pt_pkt_next(decoder, &packet, sizeof(packet));
if (errcode < 0) {
if (errcode == -pte_eos)
return 0;
return diag("error decoding packet", offset, errcode);
}
errcode = dump_one_packet(offset, &packet, tracking, options,
config);
if (errcode < 0)
return errcode;
}
}
static int dump_sync(struct pt_packet_decoder *decoder,
struct ptdump_tracking *tracking,
const struct ptdump_options *options,
const struct pt_config *config)
{
int errcode;
if (!options)
return diag("setup error", 0ull, -pte_internal);
if (options->no_sync) {
errcode = pt_pkt_sync_set(decoder, 0ull);
if (errcode < 0)
return diag("sync error", 0ull, errcode);
} else {
errcode = pt_pkt_sync_forward(decoder);
if (errcode < 0) {
if (errcode == -pte_eos)
return 0;
return diag("sync error", 0ull, errcode);
}
}
for (;;) {
errcode = dump_packets(decoder, tracking, options, config);
if (!errcode)
break;
errcode = pt_pkt_sync_forward(decoder);
if (errcode < 0) {
if (errcode == -pte_eos)
return 0;
return diag("sync error", 0ull, errcode);
}
ptdump_tracking_reset(tracking);
}
return errcode;
}
static int dump(const struct pt_config *config,
const struct ptdump_options *options)
{
struct pt_packet_decoder *decoder;
struct ptdump_tracking tracking;
int errcode;
decoder = pt_pkt_alloc_decoder(config);
if (!decoder)
return diag("failed to allocate decoder", 0ull, 0);
ptdump_tracking_init(&tracking);
errcode = dump_sync(decoder, &tracking, options, config);
ptdump_tracking_fini(&tracking);
pt_pkt_free_decoder(decoder);
return errcode;
}
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] == '-' && 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;
}
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;
}
int main(int argc, char *argv[])
{
struct ptdump_options options;
struct pt_config config;
int errcode, idx;
char *ptfile;
uint64_t pt_offset, pt_size;
ptfile = NULL;
memset(&options, 0, sizeof(options));
options.show_offset = 1;
memset(&config, 0, sizeof(config));
pt_config_init(&config);
for (idx = 1; idx < argc; ++idx) {
if (strncmp(argv[idx], "-", 1) != 0) {
ptfile = argv[idx];
if (idx < (argc-1))
return usage(argv[0]);
break;
}
if (strcmp(argv[idx], "-h") == 0)
return help(argv[0]);
if (strcmp(argv[idx], "--help") == 0)
return help(argv[0]);
if (strcmp(argv[idx], "--version") == 0)
return version(argv[0]);
if (strcmp(argv[idx], "--no-sync") == 0)
options.no_sync = 1;
else if (strcmp(argv[idx], "--quiet") == 0)
options.quiet = 1;
else if (strcmp(argv[idx], "--no-pad") == 0)
options.no_pad = 1;
else if (strcmp(argv[idx], "--no-timing") == 0)
options.no_timing = 1;
else if (strcmp(argv[idx], "--no-cyc") == 0)
options.no_cyc = 1;
else if (strcmp(argv[idx], "--no-offset") == 0)
options.show_offset = 0;
else if (strcmp(argv[idx], "--raw") == 0)
options.show_raw_bytes = 1;
else if (strcmp(argv[idx], "--lastip") == 0)
options.show_last_ip = 1;
else if (strcmp(argv[idx], "--exec-mode") == 0)
options.show_exec_mode = 1;
else if (strcmp(argv[idx], "--time") == 0) {
if (options.show_tcal) {
fprintf(stderr, "%s: specify either --time "
"or --tcal.\n", argv[0]);
return 1;
}
options.track_time = 1;
options.show_time = 1;
} else if (strcmp(argv[idx], "--time-delta") == 0) {
options.show_time_as_delta = 1;
} else if (strcmp(argv[idx], "--tcal") == 0) {
if (options.show_time) {
fprintf(stderr, "%s: specify either --time "
"or --tcal.\n", argv[0]);
return 1;
}
options.track_time = 1;
options.show_tcal = 1;
} else if (strcmp(argv[idx], "--no-tcal") == 0)
options.no_tcal = 1;
else if (strcmp(argv[idx], "--no-wall-clock") == 0)
options.no_wall_clock = 1;
else if (strcmp(argv[idx], "--cpu") == 0) {
const char *arg;
arg = argv[++idx];
if (!arg) {
fprintf(stderr,
"%s: --cpu: missing argument.\n",
argv[0]);
return 1;
}
if (strcmp(arg, "auto") == 0) {
errcode = pt_cpu_read(&config.cpu);
if (errcode < 0) {
fprintf(stderr,
"%s: error reading cpu: %s.\n",
argv[0],
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",
argv[0]);
return 1;
}
} else if (strcmp(argv[idx], "--mtc-freq") == 0) {
if (!get_arg_uint8(&config.mtc_freq, "--mtc-freq",
argv[++idx], argv[0]))
return 1;
} else if (strcmp(argv[idx], "--nom-freq") == 0) {
if (!get_arg_uint8(&config.nom_freq, "--nom-freq",
argv[++idx], argv[0]))
return 1;
} else if (strcmp(argv[idx], "--cpuid-0x15.eax") == 0) {
if (!get_arg_uint32(&config.cpuid_0x15_eax,
"--cpuid-0x15.eax", argv[++idx],
argv[0]))
return 1;
} else if (strcmp(argv[idx], "--cpuid-0x15.ebx") == 0) {
if (!get_arg_uint32(&config.cpuid_0x15_ebx,
"--cpuid-0x15.ebx", argv[++idx],
argv[0]))
return 1;
} else
return unknown_option_error(argv[idx], argv[0]);
}
if (!ptfile)
return no_file_error(argv[0]);
errcode = preprocess_filename(ptfile, &pt_offset, &pt_size);
if (errcode < 0) {
fprintf(stderr, "%s: bad file %s: %s.\n", argv[0], ptfile,
pt_errstr(pt_errcode(errcode)));
return 1;
}
errcode = pt_cpu_errata(&config.errata, &config.cpu);
if (errcode < 0)
diag("failed to determine errata", 0ull, errcode);
errcode = load_pt(&config, ptfile, pt_offset, pt_size, argv[0]);
if (errcode < 0)
return 1;
errcode = dump(&config, &options);
free(config.begin);
return -errcode;
}