/* * Copyright (c) 2013-2016, 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 "..\stdafx.h" #include "pt_cpu.h" #include "..\pt_dump.h" #include #include #include #include #include #include #define PT_VERSION_MAJOR 1 #define PT_VERSION_MINOR 5 #define PT_VERSION_BUILD 0 #define PT_VERSION_EXT "" #define CRT_SECURE_NO_WARNINGS 1 #pragma comment(lib, "libipt.lib") ptdump_global g_pt_data; static int usage(const char *name) { fprintf(stderr, "%s: [] . 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) { fprintf(stderr, "usage: %s [] [:[-]\n\n" "options:\n" " --help|-h this text.\n" " --version display version information and exit.\n" " --no-sync don't try to sync to the first PSB, assume a valid\n" " sync point at the beginning of the trace.\n" " --quiet don't print anything but errors.\n" " --no-pad don't show PAD packets.\n" " --no-timing don't show timing packets.\n" " --no-cyc don't show CYC packets and ignore them when tracking time.\n" " --no-offset don't show the offset as the first column.\n" " --raw show raw packet bytes.\n" " --lastip show last IP updates on packets with IP payloads.\n" " --exec-mode show the current execution mode on mode.exec packets.\n" " --time show the estimated TSC on timing packets.\n" " --tcal show time calibration information.\n" " --time-delta show timing information as delta.\n" " --no-tcal skip timing calibration.\n" " this will result in errors when CYC packets are encountered.\n" " --no-wall-clock suppress the no-time error and print relative time.\n" " --cpu none|auto|f/m[/s] set cpu to the given value and decode according to:\n" " none spec (default)\n" " auto current cpu\n" " f/m[/s] family/model[/stepping]\n" " --mtc-freq set the MTC frequency (IA32_RTIT_CTL[17:14]) to .\n" " --nom-freq set the nominal frequency (MSR_PLATFORM_INFO[15:8]) to .\n" " --cpuid-0x15.eax set the value of cpuid[0x15].eax.\n" " --cpuid-0x15.ebx set the value of cpuid[0x15].ebx.\n" " [:[-]] load the processor trace data from ;\n" " an optional offset or range can be given.\n", name); 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; } // Load the PT binary file: static int load_file(uint8_t **buffer, size_t *size, char *arg, const char *prog) { uint64_t begin_arg, end_arg; uint8_t *content; HANDLE hFile = NULL; HANDLE hSection = NULL; long fsize, begin, end; int range_parts; char *range; if (!buffer || !size || !arg || !prog) { fprintf(stderr, "%s: internal error.\n", prog ? prog : ""); return -1; } range_parts = 0; begin_arg = 0ull; end_arg = UINT64_MAX; range = strrchr(arg, ':'); if (range) { /* 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. */ range_parts = parse_range(range + 1, &begin_arg, &end_arg); if (range_parts <= 0) { begin_arg = 0ull; end_arg = UINT64_MAX; range_parts = 0; } else *range = 0; } errno = 0; hFile = CreateFileA(arg, FILE_GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); //fopen_s(&file, arg, "rb"); if (hFile == INVALID_HANDLE_VALUE) { fprintf(stderr, "%s: failed to open %s: %d.\n", prog, arg, errno); return -1; } fsize = SetFilePointer(hFile, 0, 0, FILE_END); if (fsize < 0) { fprintf(stderr, "%s: failed to determine size of %s: %d.\n", prog, arg, errno); goto err_file; } /* Truncate the range to fit into the file unless an explicit range end * was provided. */ if (range_parts < 2) end_arg = (uint64_t) fsize; begin = (long) begin_arg; end = (long) end_arg; if ((uint64_t) begin != begin_arg || (uint64_t) end != end_arg) { fprintf(stderr, "%s: invalid offset/range argument.\n", prog); goto err_file; } if (fsize <= begin) { fprintf(stderr, "%s: offset 0x%lx outside of %s.\n", prog, begin, arg); goto err_file; } if (fsize < end) { fprintf(stderr, "%s: range 0x%lx outside of %s.\n", prog, end, arg); goto err_file; } if (end <= begin) { fprintf(stderr, "%s: bad range.\n", prog); goto err_file; } fsize = end - begin; SetFilePointer(hFile, begin, 0, FILE_BEGIN); // Use memory mapped file hSection = CreateFileMapping((HANDLE)hFile, NULL, PAGE_READONLY, NULL, NULL, NULL); content = (uint8_t*)MapViewOfFile(hSection, FILE_MAP_READ, 0, 0, fsize); if (!content) { fprintf(stderr, "%s: failed to map the input file %s.\n", prog, arg); goto err_file; } __try { BYTE test = content[0]; } __except (EXCEPTION_EXECUTE_HANDLER) { UnmapViewOfFile((LPCVOID)content); goto err_file; } *buffer = content; *size = fsize; // Update global data on success g_pt_data.hInFile = hFile; g_pt_data.hInSection = hSection; g_pt_data.lpFileContent = (LPCVOID)content; return 0; err_file: if (hSection) CloseHandle(hSection); if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); return -1; } // Unload and free the resource of the current loaded input file static int unload_file() { if (g_pt_data.lpFileContent) UnmapViewOfFile(g_pt_data.lpFileContent); if (g_pt_data.hInSection) CloseHandle(g_pt_data.hInSection); if (g_pt_data.hInFile) CloseHandle(g_pt_data.hInFile); return 0; } // Load the binary PT file static int load_pt(struct pt_config *config, char *arg, const char *prog) { uint8_t *buffer; size_t size; int errcode; errcode = load_file(&buffer, &size, arg, 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) //TODO: Modify this to print to a file static int print_buffer(struct ptdump_buffer *buffer, uint64_t offset, const struct ptdump_options *options) { int retCode = 0; const char *sep; CHAR lpOutStr[0x200] = { 0 }; DWORD dwBytesIo = 0; 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) { sprintf_s(lpOutStr, COUNTOF(lpOutStr), "%s%-*s", lpOutStr, (int) sizeof(buffer->offset), buffer->offset); sep = " "; } if (buffer->raw[0]) { sprintf_s(lpOutStr, COUNTOF(lpOutStr), "%s%s%-*s", lpOutStr, sep, (int) sizeof(buffer->raw), buffer->raw); sep = " "; } if (buffer->payload.standard[0]) sprintf_s(lpOutStr, COUNTOF(lpOutStr), "%s%s%-*s", lpOutStr, sep, (int) sizeof(buffer->opcode), buffer->opcode); else sprintf_s(lpOutStr, COUNTOF(lpOutStr), "%s%s%s", lpOutStr, 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) sprintf_s(lpOutStr, COUNTOF(lpOutStr), " %s%s", lpOutStr, buffer->payload.extended); else if (buffer->tracking.id[0]) { sprintf_s(lpOutStr, COUNTOF(lpOutStr), "%s %-*s", lpOutStr, (int) sizeof(buffer->payload.standard), buffer->payload.standard); sprintf_s(lpOutStr, COUNTOF(lpOutStr), "%s %-*s", lpOutStr, (int) sizeof(buffer->tracking.id), buffer->tracking.id); sprintf_s(lpOutStr, COUNTOF(lpOutStr), "%s%s", lpOutStr, buffer->tracking.payload); } else if (buffer->payload.standard[0]) sprintf_s(lpOutStr, COUNTOF(lpOutStr), "%s%s", lpOutStr, buffer->payload.standard); strcat_s(lpOutStr, COUNTOF(lpOutStr), "\r\n"); if (options->hTargetFile) { retCode = WriteFile(options->hTargetFile, lpOutStr, (DWORD)strlen(lpOutStr), &dwBytesIo, NULL); // Translate the Win32 returned code in PT return code if (retCode == 0) retCode = -1; } else printf(lpOutStr); return retCode; } 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_s(pos, sizeof(buffer->raw), "%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, ""); 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, ""); else { print_field(buffer->tracking.payload, ""); 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, ""); 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, ""); 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, ""); return 0; case ppt_invalid: print_field(buffer->opcode, ""); 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); if (options->no_pge_pgd) buffer->skip = 1; 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); if (options->no_pge_pgd) buffer->skip = 1; 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); if (options->no_pip) buffer->skip = 1; 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, "0x%08" PRIx32, (DWORD)(offset + options->offset_delta)); 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 the error code and restart the scan if (pt_errcode(errcode) != pte_bad_packet) return diag("error decoding packet", offset, errcode); else return errcode; } errcode = dump_one_packet(offset, &packet, tracking, options, config); if (errcode < 0) return diag("error printing the packet", offset, errcode); } return 0; } 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) 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) return diag("sync error", 0ull, errcode); ptdump_tracking_reset(tracking); } return errcode; } // Dump all the PT packets static int pt_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; } BOOL pt_dump_file(LPTSTR lpInputFile, LPTSTR lpOutFile, DWORD dwMaxSize) { // ... Dump all the useful packets: HANDLE hTarget = NULL; CHAR binaryFileAStr[MAX_PATH] = { 0 }; CHAR myPath[MAX_PATH] = { 0 }; pt_config config = { 0 }; BOOL bRetVal = FALSE; if (!lpInputFile) return FALSE; sprintf_s((char* const)binaryFileAStr, MAX_PATH, "%S", lpInputFile); GetModuleFileNameA(GetModuleHandle(NULL), myPath, MAX_PATH); int errcode = load_pt(&config, binaryFileAStr, myPath); if (errcode < 0) return FALSE; if (lpOutFile) { // Open target file hTarget = CreateFile(lpOutFile, FILE_GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hTarget == INVALID_HANDLE_VALUE) return FALSE; else // Move to the end of file SetFilePointer(hTarget, 0, 0, FILE_END); } if (dwMaxSize == 0) dwMaxSize = (DWORD)(config.end - config.begin); bRetVal = pt_dumpW(config.begin, dwMaxSize, hTarget); unload_file(); return bRetVal; } BOOL pt_dumpW(LPBYTE lpBuff, DWORD dwBuffSize, HANDLE hOutFile, QWORD delta, BOOLEAN bTraceOnlyKernel) { ptdump_options options = { 0 }; pt_config config = { 0 }; config.size = sizeof(pt_config); config.begin = lpBuff; config.end = lpBuff + dwBuffSize; options.no_sync = 1; options.no_pad = 1; options.no_timing = 1; options.show_offset = 1; options.show_time_as_delta = 1; options.no_pge_pgd = 0; options.no_pip = (bTraceOnlyKernel != FALSE) ? 1 : 0; options.offset_delta = delta; if (hOutFile) options.hTargetFile = hOutFile; int errcode = pt_cpu_errata(&config.errata, &config.cpu); if (errcode < 0) wprintf(L"failed to determine errata (error %i)", errcode); errcode = pt_dump(&config, &options); return (errcode == 0); } 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 ptdump_main(int argc, char *argv[]) { struct ptdump_options options; struct pt_config config; int errcode, idx; char *ptfile; 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 = pt_cpu_errata(&config.errata, &config.cpu); if (errcode < 0) diag("failed to determine errata", 0ull, errcode); errcode = load_pt(&config, ptfile, argv[0]); if (errcode < 0) return errcode; errcode = pt_dump(&config, &options); free(config.begin); return -errcode; }