diff options
Diffstat (limited to 'pttc/src/parse.c')
-rw-r--r-- | pttc/src/parse.c | 2779 |
1 files changed, 2779 insertions, 0 deletions
diff --git a/pttc/src/parse.c b/pttc/src/parse.c new file mode 100644 index 0000000000000..339d159b466b5 --- /dev/null +++ b/pttc/src/parse.c @@ -0,0 +1,2779 @@ +/* + * Copyright (c) 2013-2018, 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 "errcode.h" +#include "parse.h" +#include "util.h" +#include "pt_compiler.h" + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +# define snprintf _snprintf_c +#endif + + +static const char *pt_suffix = ".pt"; +static const char *exp_suffix = ".exp"; + +#if defined(FEATURE_SIDEBAND) +static const char *sb_suffix = ".sb"; +#endif + +enum { + pd_len = 1024 +}; + +#if defined(FEATURE_SIDEBAND) + +static void sb_rename_file(struct sb_file *sb) +{ + char filename[FILENAME_MAX]; + + /* We encode the configuration in the sideband filename. */ + switch (sb->format) { + case sbf_raw: + strncpy(filename, sb->name, sizeof(filename)); + + /* Make sure @filename is terminated. */ + filename[sizeof(filename) - 1] = 0; + break; + +#if defined(FEATURE_PEVENT) + case sbf_pevent: { + const struct pev_config *config; + size_t base_len, ext_len, suffix_len, total_len; + int errcode, printed; + char extension[256]; + + config = &sb->variant.pevent.config; + + printed = snprintf(extension, sizeof(extension), + ",sample-type=0x%" PRIx64 ",time-zero=0x%" + PRIx64 ",time-shift=0x%u" ",time-mult=0x%u", + config->sample_type, config->time_zero, + config->time_shift, config->time_mult); + if (printed < 0) { + fprintf(stderr, "error renaming %s.\n", sb->name); + return; + } + + ext_len = (size_t) printed; + suffix_len = strnlen(sb_suffix, sizeof(filename)); + + base_len = strnlen(sb->name, sizeof(filename)); + base_len -= suffix_len; + + total_len = base_len + ext_len + suffix_len + 1; + if (sizeof(filename) <= total_len) { + fprintf(stderr, "warning: %s could not be renamed.\n", + sb->name); + return; + } + + strncpy(filename, sb->name, base_len); + + printed = snprintf(filename + base_len, + sizeof(filename) - base_len, "%s%s", + extension, sb_suffix); + if (printed < 0) { + fprintf(stderr, "error renaming %s.\n", sb->name); + return; + } + + errno = 0; + errcode = rename(sb->name, filename); + if (errcode < 0) + fprintf(stderr, "error renaming %s: %s.\n", + sb->name, strerror(errno)); + } + break; +#endif /* defined(FEATURE_PEVENT) */ + } + + /* Print the name of the sideband file for test.bash. */ + printf("%s\n", filename); +} + +#endif /* defined(FEATURE_SIDEBAND) */ + +/* Deallocates the memory used by @p, closes all files, clears and + * zeroes the fields. + */ +static void p_free(struct parser *p) +{ + if (!p) + return; + + yasm_free(p->y); + pd_free(p->pd); + l_free(p->pt_labels); + free(p->ptfilename); + +#if defined(FEATURE_SIDEBAND) + { + struct sb_filelist *sb; + + sb = p->sbfiles; + while (sb) { + struct sb_filelist *trash; + + trash = sb; + sb = sb->next; + + fclose(trash->sbfile.file); + + sb_rename_file(&trash->sbfile); + + free(trash->sbfile.name); + free(trash); + } + } +#endif /* defined(FEATURE_SIDEBAND) */ + + free(p); +} + +/* Initializes @p with @pttfile and @conf. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_internal if @p is the NULL pointer. + */ +static struct parser *p_alloc(const char *pttfile, const struct pt_config *conf) +{ + size_t n; + struct parser *p; + + if (!conf) + return NULL; + + if (!pttfile) + return NULL; + + p = calloc(1, sizeof(*p)); + if (!p) + return NULL; + + p->y = yasm_alloc(pttfile); + if (!p->y) + goto error; + + n = strlen(p->y->fileroot) + 1; + + p->ptfilename = malloc(n+strlen(pt_suffix)); + if (!p->ptfilename) + goto error; + + strcpy(p->ptfilename, p->y->fileroot); + strcat(p->ptfilename, pt_suffix); + + p->pd = pd_alloc(pd_len); + if (!p->pd) + goto error; + + p->pt_labels = l_alloc(); + if (!p->pt_labels) + goto error; + + p->conf = conf; + +#if defined(FEATURE_SIDEBAND) + p->sbfiles = NULL; + p->current_sbfile = NULL; +#endif + + return p; + +error: + p_free(p); + return NULL; +} + +/* Generates an .exp filename following the scheme: + * <fileroot>[-<extra>].exp + */ +static char *expfilename(struct parser *p, const char *extra) +{ + char *filename; + /* reserve enough space to hold the string + * "-cpu_fffff_mmm_sss" + 1 for the trailing null character. + */ + char cpu_suffix[19]; + size_t n; + + if (!extra) + extra = ""; + *cpu_suffix = '\0'; + + /* determine length of resulting filename, which looks like: + * <fileroot>[-<extra>][-cpu_<f>_<m>_<s>].exp + */ + n = strlen(p->y->fileroot); + + if (*extra != '\0') + /* the extra string is prepended with a -. */ + n += 1 + strlen(extra); + + if (p->conf->cpu.vendor != pcv_unknown) { + struct pt_cpu cpu; + + cpu = p->conf->cpu; + if (cpu.stepping) + n += sprintf(cpu_suffix, + "-cpu_%" PRIu16 "_%" PRIu8 "_%" PRIu8 "", + cpu.family, cpu.model, cpu.stepping); + else + n += sprintf(cpu_suffix, + "-cpu_%" PRIu16 "_%" PRIu8 "", cpu.family, + cpu.model); + } + + n += strlen(exp_suffix); + + /* trailing null character. */ + n += 1; + + filename = malloc(n); + if (!filename) + return NULL; + + strcpy(filename, p->y->fileroot); + if (*extra != '\0') { + strcat(filename, "-"); + strcat(filename, extra); + } + strcat(filename, cpu_suffix); + strcat(filename, exp_suffix); + + return filename; +} + +/* Returns true if @c is part of a label; false otherwise. */ +static int islabelchar(int c) +{ + if (isalnum(c)) + return 1; + + switch (c) { + case '_': + return 1; + } + + return 0; +} + +/* Generates the content of the .exp file by printing all lines with + * everything up to and including the first comment semicolon removed. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_internal if @p is the NULL pointer. + * Returns -err_file_write if the .exp file could not be fully written. + */ +static int p_gen_expfile(struct parser *p) +{ + int errcode; + enum { slen = 1024 }; + char s[slen]; + struct pt_directive *pd; + char *filename; + FILE *f; + + if (bug_on(!p)) + return -err_internal; + + pd = p->pd; + + /* the directive in the current line must be the .exp directive. */ + errcode = yasm_pd_parse(p->y, pd); + if (bug_on(errcode < 0)) + return -err_internal; + + if (bug_on(strcmp(pd->name, ".exp") != 0)) + return -err_internal; + + filename = expfilename(p, pd->payload); + if (!filename) + return -err_no_mem; + f = fopen(filename, "w"); + if (!f) { + free(filename); + return -err_file_open; + } + + for (;;) { + int i; + char *line, *comment; + + errcode = yasm_next_line(p->y, s, slen); + if (errcode < 0) + break; + + errcode = yasm_pd_parse(p->y, pd); + if (errcode < 0 && errcode != -err_no_directive) + break; + + if (errcode == 0 && strcmp(pd->name, ".exp") == 0) { + fclose(f); + printf("%s\n", filename); + free(filename); + filename = expfilename(p, pd->payload); + if (!filename) + return -err_no_mem; + f = fopen(filename, "w"); + if (!f) { + free(filename); + return -err_file_open; + } + continue; + } + + line = strchr(s, ';'); + if (!line) + continue; + + line += 1; + + comment = strchr(line, '#'); + if (comment) + *comment = '\0'; + + /* remove trailing spaces. */ + for (i = (int) strlen(line)-1; i >= 0 && isspace(line[i]); i--) + line[i] = '\0'; + + for (;;) { + char *tmp, label[256]; + uint64_t addr; + int zero_padding, qmark_padding, qmark_size, status; + + zero_padding = 0; + qmark_padding = 0; + qmark_size = 0; + status = 0; + + /* find the label character in the string. + * if there is no label character, we just print + * the rest of the line and end. + */ + tmp = strchr(line, '%'); + if (!tmp) { + if (fprintf(f, "%s", line) < 0) { + errcode = -err_file_write; + goto error; + } + break; + } + + /* make the label character a null byte and + * print the first portion, which does not + * belong to the label into the file. + */ + *tmp = '\0'; + if (fprintf(f, "%s", line) < 0) { + errcode = -err_file_write; + goto error; + } + + /* test if there is a valid label name after the %. */ + line = tmp+1; + if (*line == '\0' || isspace(*line)) { + errcode = -err_no_label; + goto error; + } + + /* check if zero padding is requested. */ + if (*line == '0') { + zero_padding = 1; + line += 1; + } + /* chek if ? padding is requested. */ + else if (*line == '?') { + qmark_padding = 1; + zero_padding = 1; + qmark_size = 0; + line += 1; + } + + /* advance i to the first non alpha-numeric + * character. all characters everything from + * line[0] to line[i-1] belongs to the label + * name. + */ + for (i = 0; islabelchar(line[i]); i++) + ; + + if (i > 255) { + errcode = -err_label_name; + goto error; + } + strncpy(label, line, i); + label[i] = '\0'; + + /* advance to next character. */ + line = &line[i]; + + /* lookup the label name and print it to the + * output file. + */ + errcode = yasm_lookup_label(p->y, &addr, label); + if (errcode < 0) { + errcode = l_lookup(p->pt_labels, &addr, label); + if (errcode < 0) + goto error; + + if (zero_padding) + status = fprintf(f, "%016" PRIx64, addr); + else + status = fprintf(f, "%" PRIx64, addr); + + if (status < 0) { + errcode = -err_file_write; + goto error; + } + + continue; + } + + /* check if masking is requested. */ + if (*line == '.') { + char *endptr; + unsigned long int n; + + line += 1; + + n = strtoul(line, &endptr, 0); + /* check if strtol made progress and + * stops on a space or null byte. + * otherwise the int could not be + * parsed. + */ + if (line == endptr || + (*endptr != '\0' && !isspace(*endptr) + && !ispunct(*endptr))) { + errcode = -err_parse_int; + goto error; + } + if (8 < n) { + errcode = -err_parse_int; + goto error; + } + + addr &= (1ull << (n << 3)) - 1ull; + line = endptr; + + qmark_size = (int) (8 - n); + } + + if (qmark_padding) { + for (i = 0; i < qmark_size; ++i) { + status = fprintf(f, "??"); + if (status < 0) { + errcode = -err_file_write; + goto error; + } + } + + for (; i < 8; ++i) { + uint8_t byte; + + byte = (uint8_t)(addr >> ((7 - i) * 8)); + + status = fprintf(f, "%02" PRIx8, byte); + if (status < 0) { + errcode = -err_file_write; + goto error; + } + } + } else if (zero_padding) + status = fprintf(f, "%016" PRIx64, addr); + else + status = fprintf(f, "%" PRIx64, addr); + + if (status < 0) { + errcode = -err_file_write; + goto error; + } + + } + + if (fprintf(f, "\n") < 0) { + errcode = -err_file_write; + goto error; + } + } + +error: + + fclose(f); + if (errcode < 0 && errcode != -err_out_of_range) { + fprintf(stderr, "fatal: %s could not be created:\n", filename); + yasm_print_err(p->y, "", errcode); + remove(filename); + } else + printf("%s\n", filename); + free(filename); + + /* If there are no lines left, we are done. */ + if (errcode == -err_out_of_range) + return 0; + + return errcode; +} + +static void p_close_files(struct parser *p) +{ + if (p->ptfile) { + fclose(p->ptfile); + p->ptfile = NULL; + } +} + +static int p_open_files(struct parser *p) +{ + p->ptfile = fopen(p->ptfilename, "wb"); + if (!p->ptfile) { + fprintf(stderr, "open %s failed\n", p->ptfilename); + goto error; + } + return 0; + +error: + p_close_files(p); + return -err_file_open; +} + +static int parse_mwait(uint32_t *hints, uint32_t *ext, char *payload) +{ + char *endptr; + unsigned long i; + + if (bug_on(!hints || !ext)) + return -err_internal; + + payload = strtok(payload, ","); + if (!payload || *payload == '\0') + return -err_parse_no_args; + + i = strtoul(payload, &endptr, 0); + if (payload == endptr || *endptr != '\0') + return -err_parse_int; + + if (UINT32_MAX < i) + return -err_parse_int_too_big; + + *hints = (uint32_t)i; + + payload = strtok(NULL, " ,"); + if (!payload) + return -err_parse_no_args; + + i = strtoul(payload, &endptr, 0); + if (payload == endptr || *endptr != '\0') + return -err_parse_int; + + if (UINT32_MAX < i) + return -err_parse_int_too_big; + + *ext = (uint32_t)i; + + /* no more tokens left. */ + payload = strtok(NULL, " "); + if (payload) + return -err_parse_trailing_tokens; + + return 0; +} + +static int parse_c_state(uint8_t *state, uint8_t *sub_state, const char *input) +{ + unsigned int maj, min; + int matches; + + if (!input) + return -err_parse_no_args; + + maj = 0; + min = 0; + matches = sscanf(input, " c%u.%u", &maj, &min); + switch (matches) { + case 0: + return -err_parse_no_args; + + case 2: + if (!sub_state) + return -err_parse_c_state_sub; + + if (0xf <= min) + return -err_parse_c_state_invalid; + + fallthrough; + case 1: + if (!state) + return -err_internal; + + if (0xf <= maj) + return -err_parse_c_state_invalid; + + break; + } + + *state = (uint8_t) ((maj - 1) & 0xf); + if (sub_state) + *sub_state = (uint8_t) ((min - 1) & 0xf); + + return 0; +} + +/* Processes the current directive. + * If the encoder returns an error, a message including current file and + * line number together with the pt error string is printed on stderr. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_internal if @p or @e is the NULL pointer. + * Returns -err_pt_lib if the pt encoder returned an error. + * Returns -err_parse if a general parsing error was encountered. + * Returns -err_parse_unknown_directive if there was an unknown pt directive. + */ +static int p_process_pt(struct parser *p, struct pt_encoder *e) +{ + struct pt_directive *pd; + struct pt_packet packet; + char *directive, *payload; + int bytes_written, errcode; + + if (bug_on(!p)) + return -err_internal; + + if (bug_on(!e)) + return -err_internal; + + pd = p->pd; + if (!pd) + return -err_internal; + + directive = pd->name; + payload = pd->payload; + + if (strcmp(directive, "psb") == 0) { + errcode = parse_empty(payload); + if (errcode < 0) { + yasm_print_err(p->y, "psb: parsing failed", errcode); + return errcode; + } + packet.type = ppt_psb; + } else if (strcmp(directive, "psbend") == 0) { + errcode = parse_empty(payload); + if (errcode < 0) { + yasm_print_err(p->y, "psbend: parsing failed", errcode); + return errcode; + } + packet.type = ppt_psbend; + } else if (strcmp(directive, "pad") == 0) { + errcode = parse_empty(payload); + if (errcode < 0) { + yasm_print_err(p->y, "pad: parsing failed", errcode); + return errcode; + } + packet.type = ppt_pad; + } else if (strcmp(directive, "ovf") == 0) { + errcode = parse_empty(payload); + if (errcode < 0) { + yasm_print_err(p->y, "ovf: parsing failed", errcode); + return errcode; + } + packet.type = ppt_ovf; + } else if (strcmp(directive, "stop") == 0) { + errcode = parse_empty(payload); + if (errcode < 0) { + yasm_print_err(p->y, "stop: parsing failed", errcode); + return errcode; + } + packet.type = ppt_stop; + } else if (strcmp(directive, "tnt") == 0) { + errcode = parse_tnt(&packet.payload.tnt.payload, + &packet.payload.tnt.bit_size, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tnt: parsing failed", errcode); + return errcode; + } + packet.type = ppt_tnt_8; + } else if (strcmp(directive, "tnt64") == 0) { + errcode = parse_tnt(&packet.payload.tnt.payload, + &packet.payload.tnt.bit_size, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tnt64: parsing failed", errcode); + return errcode; + } + packet.type = ppt_tnt_64; + } else if (strcmp(directive, "tip") == 0) { + errcode = parse_ip(p, &packet.payload.ip.ip, + &packet.payload.ip.ipc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tip: parsing failed", errcode); + return errcode; + } + packet.type = ppt_tip; + } else if (strcmp(directive, "tip.pge") == 0) { + errcode = parse_ip(p, &packet.payload.ip.ip, + &packet.payload.ip.ipc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tip.pge: parsing failed", + errcode); + return errcode; + } + packet.type = ppt_tip_pge; + } else if (strcmp(directive, "tip.pgd") == 0) { + errcode = parse_ip(p, &packet.payload.ip.ip, + &packet.payload.ip.ipc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tip.pgd: parsing failed", + errcode); + return errcode; + } + packet.type = ppt_tip_pgd; + } else if (strcmp(directive, "fup") == 0) { + errcode = parse_ip(p, &packet.payload.ip.ip, + &packet.payload.ip.ipc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "fup: parsing failed", errcode); + return errcode; + } + packet.type = ppt_fup; + } else if (strcmp(directive, "mode.exec") == 0) { + if (strcmp(payload, "16bit") == 0) { + packet.payload.mode.bits.exec.csl = 0; + packet.payload.mode.bits.exec.csd = 0; + } else if (strcmp(payload, "64bit") == 0) { + packet.payload.mode.bits.exec.csl = 1; + packet.payload.mode.bits.exec.csd = 0; + } else if (strcmp(payload, "32bit") == 0) { + packet.payload.mode.bits.exec.csl = 0; + packet.payload.mode.bits.exec.csd = 1; + } else { + errcode = yasm_print_err(p->y, + "mode.exec: argument must be one of \"16bit\", \"64bit\" or \"32bit\"", + -err_parse); + return errcode; + } + packet.payload.mode.leaf = pt_mol_exec; + packet.type = ppt_mode; + } else if (strcmp(directive, "mode.tsx") == 0) { + if (strcmp(payload, "begin") == 0) { + packet.payload.mode.bits.tsx.intx = 1; + packet.payload.mode.bits.tsx.abrt = 0; + } else if (strcmp(payload, "abort") == 0) { + packet.payload.mode.bits.tsx.intx = 0; + packet.payload.mode.bits.tsx.abrt = 1; + } else if (strcmp(payload, "commit") == 0) { + packet.payload.mode.bits.tsx.intx = 0; + packet.payload.mode.bits.tsx.abrt = 0; + } else { + errcode = yasm_print_err(p->y, + "mode.tsx: argument must be one of \"begin\", \"abort\" or \"commit\"", + -err_parse); + return errcode; + } + packet.payload.mode.leaf = pt_mol_tsx; + packet.type = ppt_mode; + } else if (strcmp(directive, "pip") == 0) { + const char *modifier; + + errcode = parse_uint64(&packet.payload.pip.cr3, payload); + if (errcode < 0) { + yasm_print_err(p->y, "pip: parsing failed", errcode); + return errcode; + } + packet.type = ppt_pip; + packet.payload.pip.nr = 0; + + modifier = strtok(NULL, " ,"); + if (modifier) { + if (strcmp(modifier, "nr") == 0) + packet.payload.pip.nr = 1; + else { + yasm_print_err(p->y, "pip: parsing failed", + -err_parse_trailing_tokens); + return errcode; + } + } + } else if (strcmp(directive, "tsc") == 0) { + errcode = parse_uint64(&packet.payload.tsc.tsc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tsc: parsing failed", errcode); + return errcode; + } + packet.type = ppt_tsc; + } else if (strcmp(directive, "cbr") == 0) { + errcode = parse_uint8(&packet.payload.cbr.ratio, payload); + if (errcode < 0) { + yasm_print_err(p->y, "cbr: parsing cbr failed", + errcode); + return errcode; + } + packet.type = ppt_cbr; + } else if (strcmp(directive, "tma") == 0) { + errcode = parse_tma(&packet.payload.tma.ctc, + &packet.payload.tma.fc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tma: parsing tma failed", + errcode); + return errcode; + } + packet.type = ppt_tma; + } else if (strcmp(directive, "mtc") == 0) { + errcode = parse_uint8(&packet.payload.mtc.ctc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "mtc: parsing mtc failed", + errcode); + return errcode; + } + packet.type = ppt_mtc; + } else if (strcmp(directive, "cyc") == 0) { + errcode = parse_uint64(&packet.payload.cyc.value, payload); + if (errcode < 0) { + yasm_print_err(p->y, "cyc: parsing cyc failed", + errcode); + return errcode; + } + packet.type = ppt_cyc; + } else if (strcmp(directive, "vmcs") == 0) { + errcode = parse_uint64(&packet.payload.vmcs.base, payload); + if (errcode < 0) { + yasm_print_err(p->y, "vmcs: parsing failed", errcode); + return errcode; + } + packet.type = ppt_vmcs; + } else if (strcmp(directive, "mnt") == 0) { + errcode = parse_uint64(&packet.payload.mnt.payload, payload); + if (errcode < 0) { + yasm_print_err(p->y, "mnt: parsing failed", errcode); + return errcode; + } + packet.type = ppt_mnt; + } else if (strcmp(directive, "exstop") == 0) { + packet.type = ppt_exstop; + memset(&packet.payload.exstop, 0, + sizeof(packet.payload.exstop)); + + if (strcmp(payload, "ip") == 0) + packet.payload.exstop.ip = 1; + else if (*payload) { + yasm_print_err(p->y, "exstop: parsing failed", + -err_parse_trailing_tokens); + return -err_parse_trailing_tokens; + } + } else if (strcmp(directive, "mwait") == 0) { + errcode = parse_mwait(&packet.payload.mwait.hints, + &packet.payload.mwait.ext, payload); + if (errcode < 0) { + yasm_print_err(p->y, "mwait: parsing failed", errcode); + return errcode; + } + + packet.type = ppt_mwait; + } else if (strcmp(directive, "pwre") == 0) { + char *token; + + packet.type = ppt_pwre; + memset(&packet.payload.pwre, 0, sizeof(packet.payload.pwre)); + + token = strtok(payload, " , "); + errcode = parse_c_state(&packet.payload.pwre.state, + &packet.payload.pwre.sub_state, token); + if (errcode < 0) { + yasm_print_err(p->y, "pwre: bad C-state", errcode); + return errcode; + } + + token = strtok(NULL, " ,"); + if (token) { + if (strcmp(token, "hw") == 0) + packet.payload.pwre.hw = 1; + else { + yasm_print_err(p->y, "pwre: parsing failed", + -err_parse_trailing_tokens); + return -err_parse_trailing_tokens; + } + } + } else if (strcmp(directive, "pwrx") == 0) { + char *token; + + packet.type = ppt_pwrx; + memset(&packet.payload.pwrx, 0, sizeof(packet.payload.pwrx)); + + token = strtok(payload, ":"); + if (!token) { + yasm_print_err(p->y, "pwrx: parsing failed", + -err_parse_no_args); + return -err_parse_no_args; + } + + if (strcmp(token, "int") == 0) + packet.payload.pwrx.interrupt = 1; + else if (strcmp(token, "st") == 0) + packet.payload.pwrx.store = 1; + else if (strcmp(token, "hw") == 0) + packet.payload.pwrx.autonomous = 1; + else { + yasm_print_err(p->y, "pwrx: bad wake reason", + -err_parse); + return -err_parse; + } + + token = strtok(NULL, " ,"); + errcode = parse_c_state(&packet.payload.pwrx.last, NULL, token); + if (errcode < 0) { + yasm_print_err(p->y, "pwrx: bad last C-state", errcode); + return errcode; + } + + token = strtok(NULL, " ,"); + errcode = parse_c_state(&packet.payload.pwrx.deepest, NULL, + token); + if (errcode < 0) { + yasm_print_err(p->y, "pwrx: bad deepest C-state", + errcode); + return errcode; + } + } else if (strcmp(directive, "ptw") == 0) { + char *token; + + packet.type = ppt_ptw; + memset(&packet.payload.ptw, 0, sizeof(packet.payload.ptw)); + + token = strtok(payload, ":"); + if (!token) { + yasm_print_err(p->y, "ptw: parsing failed", + -err_parse_no_args); + return -err_parse_no_args; + } + + errcode = str_to_uint8(token, &packet.payload.ptw.plc, 0); + if (errcode < 0) { + yasm_print_err(p->y, "ptw: bad payload size", errcode); + return errcode; + } + + token = strtok(NULL, ", "); + if (!token) { + yasm_print_err(p->y, "ptw: no payload", + -err_parse_no_args); + return -err_parse_no_args; + } + + errcode = str_to_uint64(token, &packet.payload.ptw.payload, 0); + if (errcode < 0) { + yasm_print_err(p->y, "ptw: bad payload", errcode); + return errcode; + } + + token = strtok(NULL, " "); + if (token) { + if (strcmp(token, "ip") != 0) { + yasm_print_err(p->y, "ptw: parse error", + -err_parse_trailing_tokens); + return -err_parse_trailing_tokens; + } + + packet.payload.ptw.ip = 1; + } + } else { + errcode = yasm_print_err(p->y, "invalid syntax", + -err_parse_unknown_directive); + return errcode; + } + + bytes_written = pt_enc_next(e, &packet); + if (bytes_written < 0) { + char msg[128]; + + snprintf(msg, sizeof(msg), + "encoder error in directive %s (status %s)", directive, + pt_errstr(pt_errcode(bytes_written))); + + yasm_print_err(p->y, msg, -err_pt_lib); + } else + p->pt_bytes_written += bytes_written; + + return bytes_written; +} + +#if defined(FEATURE_SIDEBAND) + +static int sb_open(struct parser *p, const char *fmt, const char *src, + const char *prio) +{ + struct sb_filelist *sbfiles; + const char *root; + char name[FILENAME_MAX]; + FILE *file; + + if (bug_on(!p) || bug_on(!p->y) || bug_on(!prio)) + return -err_internal; + + root = p->y->fileroot; + if (!root) { + yasm_print_err(p->y, "open - name root", -err_internal); + return -err_internal; + } + + if (src && *src) + snprintf(name, sizeof(name), "%s-%s-%s-%s%s", root, src, fmt, + prio, sb_suffix); + else + snprintf(name, sizeof(name), "%s-%s-%s%s", root, fmt, prio, + sb_suffix); + + for (sbfiles = p->sbfiles; sbfiles; sbfiles = sbfiles->next) { + if (strncmp(sbfiles->sbfile.name, name, sizeof(name)) == 0) + break; + } + + if (!sbfiles) { + file = fopen(name, "w"); + if (!file) { + yasm_print_err(p->y, name, -err_file_open); + return -err_file_open; + } + + sbfiles = malloc(sizeof(*sbfiles)); + if (!sbfiles) { + yasm_print_err(p->y, "open", -err_no_mem); + fclose(file); + return -err_no_mem; + } + + memset(&sbfiles->sbfile, 0, sizeof(sbfiles->sbfile)); + + sbfiles->sbfile.name = duplicate_str(name); + if (!sbfiles->sbfile.name) { + yasm_print_err(p->y, "open", -err_no_mem); + fclose(file); + free(sbfiles); + return -err_no_mem; + } + + sbfiles->sbfile.file = file; + sbfiles->sbfile.format = sbf_raw; + + sbfiles->next = p->sbfiles; + p->sbfiles = sbfiles; + } + + p->current_sbfile = &sbfiles->sbfile; + return 0; +} + +static struct sb_file *p_get_current_sbfile(struct parser *p) +{ + struct sb_file *sb; + + if (bug_on(!p)) + return NULL; + + sb = p->current_sbfile; + if (!sb) { + yasm_print_err(p->y, "no sideband file", -err_sb_missing); + return NULL; + } + + if (bug_on(!sb->file)) { + yasm_print_err(p->y, "corrupt sideband file", -err_internal); + return NULL; + } + + return sb; +} + +static int sb_set_format(struct parser *p, struct sb_file *sb, + enum sb_format format) +{ + if (bug_on(!p)) + return -err_internal; + + if (!sb) + return -err_sb_missing; + + switch (format) { + case sbf_raw: + /* Raw sideband directives are allowed for all formats. */ + return 0; + +#if defined(FEATURE_PEVENT) + case sbf_pevent: + switch (sb->format) { + case sbf_pevent: + return 0; + + case sbf_raw: + sb->format = sbf_pevent; + + memset(&sb->variant.pevent, 0, + sizeof(sb->variant.pevent)); + sb->variant.pevent.config.size = + sizeof(sb->variant.pevent.config); + sb->variant.pevent.config.time_shift = 0; + sb->variant.pevent.config.time_mult = 1; + sb->variant.pevent.config.time_zero = 0ull; + return 0; + + default: + yasm_print_err(p->y, "mixing sideband formats", + -err_sb_mix); + return -err_sb_mix; + } +#endif /* defined(FEATURE_PEVENT) */ + } + + yasm_print_err(p->y, "unknown sideband format", -err_internal); + return -err_internal; +} + +static int sb_raw(struct parser *p, const void *buffer, size_t size) +{ + struct sb_file *sb; + size_t written; + int errcode; + + if (bug_on(!p)) + return -err_internal; + + sb = p_get_current_sbfile(p); + if (!sb) + return -err_sb_missing; + + errcode = sb_set_format(p, sb, sbf_raw); + if (errcode < 0) + return errcode; + + written = fwrite(buffer, size, 1, sb->file); + if (written != 1) { + yasm_print_err(p->y, "write failed", -err_file_write); + return -err_file_write; + } + + sb->bytes_written += (int) size; + return 0; +} + +static int sb_raw_8(struct parser *p, uint8_t value) +{ + return sb_raw(p, &value, sizeof(value)); +} + +static int sb_raw_16(struct parser *p, uint16_t value) +{ + return sb_raw(p, &value, sizeof(value)); +} + +static int sb_raw_32(struct parser *p, uint32_t value) +{ + return sb_raw(p, &value, sizeof(value)); +} + +static int sb_raw_64(struct parser *p, uint64_t value) +{ + return sb_raw(p, &value, sizeof(value)); +} + +#if defined(FEATURE_PEVENT) + +/* A buffer to hold sample values to which a pev_event can point. */ + +struct pev_sample_buffer { + uint32_t pid; + uint32_t tid; + uint64_t time; + uint64_t id; + uint64_t stream_id; + uint32_t cpu; + uint64_t identifier; +}; + +static int pevent_sample_type(struct parser *p, uint64_t sample_type) +{ + struct sb_file *sb; + int errcode; + + sb = p_get_current_sbfile(p); + if (!sb) + return -err_sb_missing; + + errcode = sb_set_format(p, sb, sbf_pevent); + if (errcode < 0) + return errcode; + + if (sb->variant.pevent.is_final) { + yasm_print_err(p->y, + "the sideband configuration can no longer be " + "modified", -err_sb_final); + return -err_sb_final; + } + + sb->variant.pevent.config.sample_type = sample_type; + return 0; +} + +static int pevent_process_samples(struct pev_event *event, + struct pev_sample_buffer *samples, + struct parser *p, + const struct pev_config *config, + char *payload) +{ + char *token; + + if (bug_on(!event) || bug_on(!samples) || bug_on(!config)) + return -err_internal; + + if (config->sample_type & PERF_SAMPLE_TID) { + int errcode; + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + payload = NULL; + + errcode = str_to_uint32(token, &samples->pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad pid", errcode); + return errcode; + } + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + errcode = str_to_uint32(token, &samples->tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad tid", errcode); + return errcode; + } + + event->sample.pid = &samples->pid; + event->sample.tid = &samples->tid; + } + + if (config->sample_type & PERF_SAMPLE_TIME) { + int errcode; + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "tsc missing", -err_parse); + return -err_parse; + } + + payload = NULL; + + errcode = str_to_uint64(token, &event->sample.tsc, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad tsc", errcode); + return errcode; + } + + errcode = pev_time_from_tsc(&samples->time, event->sample.tsc, + config); + if (errcode < 0) { + fprintf(stderr, "error converting tsc %"PRIx64": %s\n", + event->sample.tsc, + pt_errstr(pt_errcode(errcode))); + return -err_pt_lib; + } + + event->sample.time = &samples->time; + } + + if (config->sample_type & PERF_SAMPLE_ID) { + int errcode; + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "id missing", -err_parse); + return -err_parse; + } + + payload = NULL; + + errcode = str_to_uint64(token, &samples->id, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad id", errcode); + return errcode; + } + + event->sample.id = &samples->id; + } + + if (config->sample_type & PERF_SAMPLE_CPU) { + int errcode; + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "cpu missing", -err_parse); + return -err_parse; + } + + payload = NULL; + + errcode = str_to_uint32(token, &samples->cpu, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad cpu", errcode); + return errcode; + } + + event->sample.cpu = &samples->cpu; + } + + if (config->sample_type & PERF_SAMPLE_STREAM_ID) { + int errcode; + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "stream missing", -err_parse); + return -err_parse; + } + + payload = NULL; + + errcode = str_to_uint64(token, &samples->stream_id, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad stream", errcode); + return errcode; + } + + event->sample.stream_id = &samples->stream_id; + } + + if (config->sample_type & PERF_SAMPLE_IDENTIFIER) { + int errcode; + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "identifier missing", -err_parse); + return -err_parse; + } + + payload = NULL; + + errcode = str_to_uint64(token, &samples->identifier, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad identifier", errcode); + return errcode; + } + + event->sample.identifier = &samples->identifier; + } + + token = strtok(payload, " ,"); + if (token) { + yasm_print_err(p->y, "unexpected samples", -err_parse); + return -err_parse; + } + + return 0; +} + +static int sb_pevent(struct parser *p, struct pev_event *event, char *payload) +{ + const struct pev_config *config; + struct pev_sample_buffer samples; + struct sb_file *sb; + uint8_t raw[FILENAME_MAX]; + int errcode, size; + + memset(raw, 0, sizeof(raw)); + + sb = p_get_current_sbfile(p); + if (!sb) + return -err_sb_missing; + + errcode = sb_set_format(p, sb, sbf_pevent); + if (errcode < 0) + return errcode; + + config = &sb->variant.pevent.config; + + errcode = pevent_process_samples(event, &samples, p, config, payload); + if (errcode < 0) + return errcode; + + size = pev_write(event, raw, raw + sizeof(raw), config); + if (size < 0) { + fprintf(stderr, "error writing pevent sample: %s\n", + pt_errstr(pt_errcode(size))); + return -err_pt_lib; + } + + /* Emitting a pevent sideband event finalizes the configuration. */ + sb->variant.pevent.is_final = 1; + + return sb_raw(p, raw, size); +} + +static int pevent_mmap_section(struct parser *p, const char *section, + const char *pid, const char *tid) +{ + union { + struct pev_record_mmap mmap; + uint8_t buffer[FILENAME_MAX]; + } record; + struct pev_event event; + const char *filename; + uint64_t start, org; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + filename = p->y->binfile; + if (!filename) { + yasm_print_err(p->y, "pevent-mmap-section - filename", + -err_internal); + return -err_internal; + } + + strncpy(record.mmap.filename, filename, + sizeof(record.buffer) - sizeof(record.mmap)); + + errcode = str_to_uint32(pid, &record.mmap.pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - pid", errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.mmap.tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - tid", errcode); + return errcode; + } + + errcode = yasm_lookup_section_label(p->y, section, "vstart", + &record.mmap.addr); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - section vstart", + errcode); + return errcode; + } + + errcode = yasm_lookup_section_label(p->y, section, "length", + &record.mmap.len); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - section length", + errcode); + return errcode; + } + + errcode = yasm_lookup_section_label(p->y, section, "start", + &start); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - section start", + errcode); + return errcode; + } + + errcode = yasm_lookup_label(p->y, &org, "org"); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - org", + errcode); + return errcode; + } + + if (start < org) { + yasm_print_err(p->y, "corrupt section labels", -err_internal); + return -err_internal; + } + + record.mmap.pgoff = start - org; + + event.type = PERF_RECORD_MMAP; + event.record.mmap = &record.mmap; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_mmap(struct parser *p, const char *pid, const char *tid, + const char *addr, const char *len, const char *pgoff, + const char *filename) +{ + union { + struct pev_record_mmap mmap; + uint8_t buffer[FILENAME_MAX]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint32(pid, &record.mmap.pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - pid", errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.mmap.tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - tid", errcode); + return errcode; + } + + errcode = str_to_uint64(addr, &record.mmap.addr, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - addr", errcode); + return errcode; + } + + errcode = str_to_uint64(len, &record.mmap.len, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - len", errcode); + return errcode; + } + + errcode = str_to_uint64(pgoff, &record.mmap.pgoff, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - pgoff", errcode); + return errcode; + } + + strncpy(record.mmap.filename, filename, + sizeof(record.buffer) - sizeof(record.mmap)); + + event.type = PERF_RECORD_MMAP; + event.record.mmap = &record.mmap; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_lost(struct parser *p, const char *id, const char *lost) +{ + union { + struct pev_record_lost lost; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint64(id, &record.lost.id, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-lost - id", errcode); + return errcode; + } + + errcode = str_to_uint64(lost, &record.lost.lost, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-lost - lost", errcode); + return errcode; + } + + event.type = PERF_RECORD_LOST; + event.record.lost = &record.lost; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_comm(struct parser *p, const char *pid, const char *tid, + const char *comm, uint16_t misc) +{ + union { + struct pev_record_comm comm; + uint8_t buffer[FILENAME_MAX]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint32(pid, &record.comm.pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-comm - pid", errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.comm.tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-comm - tid", errcode); + return errcode; + } + + strcpy(record.comm.comm, comm); + + event.type = PERF_RECORD_COMM; + event.misc = misc; + event.record.comm = &record.comm; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_exit(struct parser *p, const char *pid, const char *ppid, + const char *tid, const char *ptid, const char *time) +{ + union { + struct pev_record_exit exit; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint32(pid, &record.exit.pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-exit - pid", errcode); + return errcode; + } + + errcode = str_to_uint32(ppid, &record.exit.ppid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-exit - ppid", errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.exit.tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-exit - tid", errcode); + return errcode; + } + + errcode = str_to_uint32(ptid, &record.exit.ptid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-exit - ptid", errcode); + return errcode; + } + + + errcode = str_to_uint64(time, &record.exit.time, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-exit - time", errcode); + return errcode; + } + + event.type = PERF_RECORD_EXIT; + event.record.exit = &record.exit; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_fork(struct parser *p, const char *pid, const char *ppid, + const char *tid, const char *ptid, const char *time) +{ + union { + struct pev_record_fork fork; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint32(pid, &record.fork.pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-fork - pid", errcode); + return errcode; + } + + errcode = str_to_uint32(ppid, &record.fork.ppid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-fork - ppid", errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.fork.tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-fork - tid", errcode); + return errcode; + } + + errcode = str_to_uint32(ptid, &record.fork.ptid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-fork - ptid", errcode); + return errcode; + } + + + errcode = str_to_uint64(time, &record.fork.time, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-fork - time", errcode); + return errcode; + } + + event.type = PERF_RECORD_FORK; + event.record.fork = &record.fork; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_aux(struct parser *p, const char *offset, const char *size, + const char *flags) +{ + union { + struct pev_record_aux aux; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint64(offset, &record.aux.aux_offset, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-aux - offset", errcode); + return errcode; + } + + errcode = str_to_uint64(size, &record.aux.aux_size, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-aux - size", errcode); + return errcode; + } + + errcode = str_to_uint64(flags, &record.aux.flags, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-aux - flags", errcode); + return errcode; + } + + event.type = PERF_RECORD_AUX; + event.record.aux = &record.aux; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_itrace_start(struct parser *p, const char *pid, + const char *tid) +{ + union { + struct pev_record_itrace_start itrace_start; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint32(pid, &record.itrace_start.pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-itrace-start - pid", errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.itrace_start.tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-itrace-start - tid", errcode); + return errcode; + } + + event.type = PERF_RECORD_ITRACE_START; + event.record.itrace_start = &record.itrace_start; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_lost_samples(struct parser *p, const char *lost) +{ + union { + struct pev_record_lost_samples lost_samples; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint64(lost, &record.lost_samples.lost, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-lost-samples - lost", errcode); + return errcode; + } + + event.type = PERF_RECORD_LOST_SAMPLES; + event.record.lost_samples = &record.lost_samples; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_switch(struct parser *p, uint16_t misc, char *payload) +{ + struct pev_event event; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(&event, 0, sizeof(event)); + + event.type = PERF_RECORD_SWITCH; + event.misc = misc; + + return sb_pevent(p, &event, payload); +} + +static int pevent_switch_cpu_wide(struct parser *p, const char *pid, + const char *tid, uint16_t misc) +{ + union { + struct pev_record_switch_cpu_wide switch_cpu_wide; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + event.misc = misc; + + errcode = str_to_uint32(pid, &record.switch_cpu_wide.next_prev_pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, + "pevent-switch-cpu-wide - next_prev_pid", + errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.switch_cpu_wide.next_prev_tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, + "pevent-switch-cpu-wide - next_prev_tid", + errcode); + return errcode; + } + + event.type = PERF_RECORD_SWITCH_CPU_WIDE; + event.record.switch_cpu_wide = &record.switch_cpu_wide; + + return sb_pevent(p, &event, NULL); +} +#endif /* defined(FEATURE_PEVENT) */ + +/* Process a @sb directive. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_internal if @p is the NULL pointer. + * Returns -err_parse if a general parsing error was encountered. + * Returns -err_parse_unknown_directive if there was an unknown pt directive. + */ +static int p_process_sb(struct parser *p) +{ + struct pt_directive *pd; + char *directive, *payload; + + if (bug_on(!p)) + return -err_internal; + + pd = p->pd; + if (!pd) + return -err_internal; + + directive = pd->name; + payload = pd->payload; + + if (strcmp(directive, "primary") == 0) { + char *fmt, *src; + + fmt = strtok(payload, " ,"); + if (!fmt) { + yasm_print_err(p->y, "primary - format missing", + -err_parse_no_args); + return -err_parse_no_args; + } + + src = strtok(NULL, " "); + + return sb_open(p, fmt, src, "primary"); + } else if (strcmp(directive, "secondary") == 0) { + char *fmt, *src; + + fmt = strtok(payload, " ,"); + if (!fmt) { + yasm_print_err(p->y, "secondary - format missing", + -err_parse_no_args); + return -err_parse_no_args; + } + + src = strtok(NULL, " "); + + return sb_open(p, fmt, src, "secondary"); + } else if (strcmp(directive, "raw-8") == 0) { + uint8_t value; + int errcode; + + errcode = parse_uint8(&value, payload); + if (errcode < 0) { + yasm_print_err(p->y, payload, errcode); + return errcode; + } + + return sb_raw_8(p, value); + } else if (strcmp(directive, "raw-16") == 0) { + uint16_t value; + int errcode; + + errcode = parse_uint16(&value, payload); + if (errcode < 0) { + yasm_print_err(p->y, payload, errcode); + return errcode; + } + + return sb_raw_16(p, value); + } else if (strcmp(directive, "raw-32") == 0) { + uint32_t value; + int errcode; + + errcode = parse_uint32(&value, payload); + if (errcode < 0) { + yasm_print_err(p->y, payload, errcode); + return errcode; + } + + return sb_raw_32(p, value); + } else if (strcmp(directive, "raw-64") == 0) { + uint64_t value; + int errcode; + + errcode = parse_uint64(&value, payload); + if (errcode < 0) { + yasm_print_err(p->y, payload, errcode); + return errcode; + } + + return sb_raw_64(p, value); +#if defined(FEATURE_PEVENT) + } else if (strcmp(directive, "pevent-sample_type") == 0) { + uint64_t sample_type; + char *token; + + sample_type = 0ull; + + for (token = strtok(payload, " ,"); token; + token = strtok(NULL, " ,")) { + + if (strcmp(token, "tid") == 0) + sample_type |= (uint64_t) PERF_SAMPLE_TID; + else if (strcmp(token, "time") == 0) + sample_type |= (uint64_t) PERF_SAMPLE_TIME; + else if (strcmp(token, "id") == 0) + sample_type |= (uint64_t) PERF_SAMPLE_ID; + else if (strcmp(token, "stream") == 0) + sample_type |= (uint64_t) PERF_SAMPLE_STREAM_ID; + else if (strcmp(token, "cpu") == 0) + sample_type |= (uint64_t) PERF_SAMPLE_CPU; + else if (strcmp(token, "identifier") == 0) + sample_type |= + (uint64_t) PERF_SAMPLE_IDENTIFIER; + else { + uint64_t value; + int errcode; + + errcode = parse_uint64(&value, payload); + if (errcode < 0) { + yasm_print_err(p->y, token, errcode); + return errcode; + } + + sample_type |= value; + } + } + + return pevent_sample_type(p, sample_type); + + } else if (strcmp(directive, "pevent-mmap-section") == 0) { + char *section, *pid, *tid; + + section = strtok(payload, " ,"); + if (!section) { + yasm_print_err(p->y, "section missing", -err_parse); + return -err_parse; + } + + pid = strtok(NULL, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + return pevent_mmap_section(p, section, pid, tid); + } else if (strcmp(directive, "pevent-mmap") == 0) { + char *pid, *tid, *addr, *len, *pgoff, *filename; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + addr = strtok(NULL, " ,"); + if (!addr) { + yasm_print_err(p->y, "addr missing", -err_parse); + return -err_parse; + } + + len = strtok(NULL, " ,"); + if (!len) { + yasm_print_err(p->y, "len missing", -err_parse); + return -err_parse; + } + + pgoff = strtok(NULL, " ,"); + if (!pgoff) { + yasm_print_err(p->y, "pgoff missing", -err_parse); + return -err_parse; + } + + filename = strtok(NULL, " ,"); + if (!filename) { + yasm_print_err(p->y, "filename missing", -err_parse); + return -err_parse; + } + + return pevent_mmap(p, pid, tid, addr, len, pgoff, filename); + } else if (strcmp(directive, "pevent-lost") == 0) { + char *id, *lost; + + id = strtok(payload, " ,"); + if (!id) { + yasm_print_err(p->y, "id missing", -err_parse); + return -err_parse; + } + + lost = strtok(NULL, " ,"); + if (!lost) { + yasm_print_err(p->y, "lost missing", -err_parse); + return -err_parse; + } + + return pevent_lost(p, id, lost); + } else if (strcmp(directive, "pevent-comm") == 0) { + char *pid, *tid, *comm; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + comm = strtok(NULL, " ,"); + if (!comm) { + yasm_print_err(p->y, "comm missing", -err_parse); + return -err_parse; + } + + return pevent_comm(p, pid, tid, comm, 0); + } else if (strcmp(directive, "pevent-comm.exec") == 0) { + char *pid, *tid, *comm; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + comm = strtok(NULL, " ,"); + if (!comm) { + yasm_print_err(p->y, "comm missing", -err_parse); + return -err_parse; + } + + return pevent_comm(p, pid, tid, comm, + PERF_RECORD_MISC_COMM_EXEC); + } else if (strcmp(directive, "pevent-exit") == 0) { + char *pid, *ppid, *tid, *ptid, *time; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + ppid = strtok(NULL, " ,"); + if (!ppid) { + yasm_print_err(p->y, "ppid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + ptid = strtok(NULL, " ,"); + if (!ptid) { + yasm_print_err(p->y, "ptid missing", -err_parse); + return -err_parse; + } + + time = strtok(NULL, " ,"); + if (!time) { + yasm_print_err(p->y, "time missing", -err_parse); + return -err_parse; + } + + return pevent_exit(p, pid, ppid, tid, ptid, time); + } else if (strcmp(directive, "pevent-fork") == 0) { + char *pid, *ppid, *tid, *ptid, *time; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + ppid = strtok(NULL, " ,"); + if (!ppid) { + yasm_print_err(p->y, "ppid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + ptid = strtok(NULL, " ,"); + if (!ptid) { + yasm_print_err(p->y, "ptid missing", -err_parse); + return -err_parse; + } + + time = strtok(NULL, " ,"); + if (!time) { + yasm_print_err(p->y, "time missing", -err_parse); + return -err_parse; + } + + return pevent_fork(p, pid, ppid, tid, ptid, time); + } else if (strcmp(directive, "pevent-aux") == 0) { + char *offset, *size, *flags; + + offset = strtok(payload, " ,"); + if (!offset) { + yasm_print_err(p->y, "offset missing", -err_parse); + return -err_parse; + } + + size = strtok(NULL, " ,"); + if (!size) { + yasm_print_err(p->y, "size missing", -err_parse); + return -err_parse; + } + + flags = strtok(NULL, " ,"); + if (!flags) { + yasm_print_err(p->y, "flags missing", -err_parse); + return -err_parse; + } + + return pevent_aux(p, offset, size, flags); + } else if (strcmp(directive, "pevent-itrace-start") == 0) { + char *pid, *tid; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + return pevent_itrace_start(p, pid, tid); + } else if (strcmp(directive, "pevent-lost-samples") == 0) { + char *lost; + + lost = strtok(payload, " ,"); + if (!lost) { + yasm_print_err(p->y, "lost missing", -err_parse); + return -err_parse; + } + + return pevent_lost_samples(p, lost); + } else if (strcmp(directive, "pevent-switch.in") == 0) + return pevent_switch(p, 0u, payload); + else if (strcmp(directive, "pevent-switch.out") == 0) + return pevent_switch(p, PERF_RECORD_MISC_SWITCH_OUT, payload); + else if (strcmp(directive, "pevent-switch-cpu-wide.in") == 0) { + char *pid, *tid; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + return pevent_switch_cpu_wide(p, pid, tid, 0u); + } else if (strcmp(directive, "pevent-switch-cpu-wide.out") == 0) { + char *pid, *tid; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + return pevent_switch_cpu_wide(p, pid, tid, + PERF_RECORD_MISC_SWITCH_OUT); +#endif /* defined(FEATURE_PEVENT) */ + } else { + yasm_print_err(p->y, "syntax error", + -err_parse_unknown_directive); + return -err_parse_unknown_directive; + } +} + +#endif /* defined(FEATURE_SIDEBAND) */ + +/* Processes the current directive. + * If the encoder returns an error, a message including current file and + * line number together with the pt error string is printed on stderr. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_internal if @p or @e is the NULL pointer. + * Returns -err_parse_missing_directive if there was a pt directive marker, + * but no directive. + * Returns -stop_process if the .exp directive was encountered. + * Returns -err_pt_lib if the pt encoder returned an error. + * Returns -err_parse if a general parsing error was encountered. + * Returns -err_parse_unknown_directive if there was an unknown pt directive. + */ +static int p_process(struct parser *p, struct pt_encoder *e) +{ + char *directive, *tmp; + struct pt_directive *pd; + + if (bug_on(!p)) + return -err_internal; + + pd = p->pd; + if (!pd) + return -err_internal; + + directive = pd->name; + + /* We must have a directive. */ + if (!directive || (strcmp(directive, "") == 0)) + return yasm_print_err(p->y, "invalid syntax", + -err_parse_missing_directive); + + /* Check for special directives - they won't contain labels. */ + if (strcmp(directive, ".exp") == 0) { + int errcode; + + /* this is the end of processing pt directives, so we + * add a p_last label to the pt directive labels. + */ + errcode = l_append(p->pt_labels, "eos", p->pt_bytes_written); + if (errcode < 0) + return yasm_print_err(p->y, "append label", errcode); + + return -stop_process; + } + + /* find a label name. */ + tmp = strchr(directive, ':'); + if (tmp) { + char *pt_label_name; + uint64_t x; + int errcode, bytes_written; + size_t len; + + pt_label_name = directive; + directive = tmp+1; + *tmp = '\0'; + + /* ignore whitespace between label and directive. */ + while (isspace(*directive)) + directive += 1; + + /* we must have a directive, not just a label. */ + if (strcmp(directive, "") == 0) + return yasm_print_err(p->y, "invalid syntax", + -err_parse_missing_directive); + + /* if we can lookup a yasm label with the same name, the + * current pt directive label is invalid. */ + errcode = yasm_lookup_label(p->y, &x, pt_label_name); + if (errcode == 0) + errcode = -err_label_not_unique; + + if (errcode != -err_no_label) + return yasm_print_err(p->y, "label lookup", + errcode); + + /* if we can lookup a pt directive label with the same + * name, the current pt directive label is invalid. */ + errcode = l_lookup(p->pt_labels, &x, pt_label_name); + if (errcode == 0) + errcode = -err_label_not_unique; + + if (errcode != -err_no_label) + return yasm_print_err(p->y, "label lookup", + -err_label_not_unique); + + bytes_written = -pte_internal; + switch (pd->kind) { + case pdk_pt: + bytes_written = p->pt_bytes_written; + break; + +#if defined(FEATURE_SIDEBAND) + case pdk_sb: { + struct sb_file *sb; + + sb = p_get_current_sbfile(p); + if (!sb) + return yasm_print_err(p->y, "sideband label", + -err_sb_missing); + + bytes_written = sb->bytes_written; + } + break; +#endif /* defined(FEATURE_SIDEBAND) */ + } + + if (bytes_written < 0) + return bytes_written; + + errcode = l_append(p->pt_labels, pt_label_name, bytes_written); + if (errcode < 0) + return errcode; + + /* Update the directive name in the parser. */ + len = strlen(directive) + 1; + memmove(pd->name, directive, len); + } + + switch (pd->kind) { + case pdk_pt: + return p_process_pt(p, e); + +#if defined(FEATURE_SIDEBAND) + case pdk_sb: + return p_process_sb(p); +#endif + } + + return -err_internal; +} + +/* Starts the parsing process. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_pt_lib if the pt encoder could not be initialized. + * Returns -err_file_write if the .pt or .exp file could not be fully + * written. + */ +static int p_start(struct parser *p) +{ + int errcode; + + if (bug_on(!p)) + return -err_internal; + + errcode = yasm_parse(p->y); + if (errcode < 0) + return errcode; + + for (;;) { + int bytes_written; + struct pt_encoder *e; + + errcode = yasm_next_pt_directive(p->y, p->pd); + if (errcode < 0) + break; + + e = pt_alloc_encoder(p->conf); + if (!e) { + fprintf(stderr, "pt_alloc_encoder failed\n"); + errcode = -err_pt_lib; + break; + } + + bytes_written = p_process(p, e); + + pt_free_encoder(e); + + if (bytes_written == -stop_process) { + errcode = p_gen_expfile(p); + break; + } + if (bytes_written < 0) { + errcode = bytes_written; + break; + } + if (fwrite(p->conf->begin, 1, bytes_written, p->ptfile) + != (size_t)bytes_written) { + fprintf(stderr, "write %s failed", p->ptfilename); + errcode = -err_file_write; + break; + } + } + + /* If there is no directive left, there's nothing more to do. */ + if (errcode == -err_no_directive) + return 0; + + return errcode; +} + +int parse(const char *pttfile, const struct pt_config *conf) +{ + int errcode; + struct parser *p; + + p = p_alloc(pttfile, conf); + if (!p) + return -err_no_mem; + + errcode = p_open_files(p); + if (errcode < 0) + goto error; + + errcode = p_start(p); + p_close_files(p); + +error: + p_free(p); + return errcode; +} + +int parse_empty(char *payload) +{ + if (!payload) + return 0; + + strtok(payload, " "); + if (!payload || *payload == '\0') + return 0; + + return -err_parse_trailing_tokens; +} + +int parse_tnt(uint64_t *tnt, uint8_t *size, char *payload) +{ + char c; + + if (bug_on(!size)) + return -err_internal; + + if (bug_on(!tnt)) + return -err_internal; + + *size = 0; + *tnt = 0ull; + + if (!payload) + return 0; + + while (*payload != '\0') { + c = *payload; + payload++; + if (isspace(c) || c == '.') + continue; + *size += 1; + *tnt <<= 1; + switch (c) { + case 'n': + break; + case 't': + *tnt |= 1; + break; + default: + return -err_parse_unknown_char; + } + } + + return 0; +} + +static int ipc_from_uint32(enum pt_ip_compression *ipc, uint32_t val) +{ + switch (val) { + case pt_ipc_suppressed: + case pt_ipc_update_16: + case pt_ipc_update_32: + case pt_ipc_update_48: + case pt_ipc_sext_48: + case pt_ipc_full: + *ipc = (enum pt_ip_compression) val; + return 0; + } + return -err_parse_ipc; +} + +int parse_ip(struct parser *p, uint64_t *ip, enum pt_ip_compression *ipc, + char *payload) +{ + uint32_t ipcval; + int errcode; + + if (bug_on(!ip)) + return -err_internal; + + if (bug_on(!ipc)) + return -err_internal; + + *ipc = pt_ipc_suppressed; + *ip = 0; + + payload = strtok(payload, " :"); + if (!payload || *payload == '\0') + return -err_parse_no_args; + + errcode = str_to_uint32(payload, &ipcval, 0); + if (errcode < 0) + return errcode; + + errcode = ipc_from_uint32(ipc, ipcval); + if (errcode < 0) + return errcode; + + payload = strtok(NULL, " :"); + if (!payload) + return -err_parse_ip_missing; + + /* can be resolved to a label? */ + if (*payload == '%') { + if (!p) + return -err_internal; + + errcode = yasm_lookup_label(p->y, ip, payload + 1); + if (errcode < 0) + return errcode; + } else { + /* can be parsed as address? */ + errcode = str_to_uint64(payload, ip, 0); + if (errcode < 0) + return errcode; + } + + /* no more tokens left. */ + payload = strtok(NULL, " "); + if (payload) + return -err_parse_trailing_tokens; + + return 0; +} + +int parse_uint64(uint64_t *x, char *payload) +{ + int errcode; + + if (bug_on(!x)) + return -err_internal; + + payload = strtok(payload, " ,"); + if (!payload) + return -err_parse_no_args; + + errcode = str_to_uint64(payload, x, 0); + if (errcode < 0) + return errcode; + + return 0; +} + +int parse_uint32(uint32_t *x, char *payload) +{ + int errcode; + + if (bug_on(!x)) + return -err_internal; + + payload = strtok(payload, " ,"); + if (!payload) + return -err_parse_no_args; + + errcode = str_to_uint32(payload, x, 0); + if (errcode < 0) + return errcode; + + return 0; +} + +int parse_uint16(uint16_t *x, char *payload) +{ + int errcode; + + if (bug_on(!x)) + return -err_internal; + + payload = strtok(payload, " ,"); + if (!payload) + return -err_parse_no_args; + + errcode = str_to_uint16(payload, x, 0); + if (errcode < 0) + return errcode; + + return 0; +} + +int parse_uint8(uint8_t *x, char *payload) +{ + int errcode; + + if (bug_on(!x)) + return -err_internal; + + payload = strtok(payload, " ,"); + if (!payload) + return -err_parse_no_args; + + errcode = str_to_uint8(payload, x, 0); + if (errcode < 0) + return errcode; + + return 0; +} + +int parse_tma(uint16_t *ctc, uint16_t *fc, char *payload) +{ + char *endptr; + long int i; + + if (bug_on(!ctc || !fc)) + return -err_internal; + + payload = strtok(payload, ","); + if (!payload || *payload == '\0') + return -err_parse_no_args; + + i = strtol(payload, &endptr, 0); + if (payload == endptr || *endptr != '\0') + return -err_parse_int; + + if (i > 0xffffl) + return -err_parse_int_too_big; + + *ctc = (uint16_t)i; + + payload = strtok(NULL, " ,"); + if (!payload) + return -err_parse_no_args; + + i = strtol(payload, &endptr, 0); + if (payload == endptr || *endptr != '\0') + return -err_parse_int; + + if (i > 0xffffl) + return -err_parse_int_too_big; + + *fc = (uint16_t)i; + + /* no more tokens left. */ + payload = strtok(NULL, " "); + if (payload) + return -err_parse_trailing_tokens; + + return 0; +} |