| /* |
| * Copyright (c) 2013-2019, Intel Corporation |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * * Neither the name of Intel Corporation nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "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) - 1); |
| |
| /* 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)); |
| if (base_len < suffix_len) { |
| fprintf(stderr, "error renaming %s.\n", sb->name); |
| return; |
| } |
| |
| 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; |
| int len; |
| |
| cpu = p->conf->cpu; |
| if (cpu.stepping) |
| len = sprintf(cpu_suffix, |
| "-cpu_%" PRIu16 "_%" PRIu8 "_%" PRIu8 "", |
| cpu.family, cpu.model, cpu.stepping); |
| else |
| len = sprintf(cpu_suffix, |
| "-cpu_%" PRIu16 "_%" PRIu8 "", cpu.family, |
| cpu.model); |
| |
| if (len < 0) |
| return NULL; |
| |
| n += (size_t) len; |
| } |
| |
| 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, (size_t) 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 report_lib_error(struct parser *p, const char *message, int errcode) |
| { |
| char buffer[128]; |
| |
| if (bug_on(!p)) |
| return err_internal; |
| |
| snprintf(buffer, sizeof(buffer), "%s: %s", message, |
| pt_errstr(pt_errcode(errcode))); |
| |
| yasm_print_err(p->y, buffer, -err_pt_lib); |
| |
| return -err_pt_lib; |
| } |
| |
| static int parse_mwait(uint32_t *hints, uint32_t *ext, char *payload) |
| { |
| int errcode; |
| |
| if (bug_on(!hints || !ext)) |
| return -err_internal; |
| |
| payload = strtok(payload, ","); |
| if (!payload || *payload == '\0') |
| return -err_parse_no_args; |
| |
| errcode = str_to_uint32(payload, hints, 0); |
| if (errcode < 0) |
| return errcode; |
| |
| payload = strtok(NULL, " ,"); |
| if (!payload) |
| return -err_parse_no_args; |
| |
| errcode = str_to_uint32(payload, ext, 0); |
| if (errcode < 0) |
| return errcode; |
| |
| /* 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) { |
| default: |
| return -err_parse; |
| |
| 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; |
| } |
| |
| static int pt_raw(struct parser *p, struct pt_encoder *e, |
| const void *buffer, size_t size) |
| { |
| const struct pt_config *conf; |
| uint8_t *begin, *end, *pos, *start; |
| uint64_t offset; |
| int errcode; |
| |
| if (bug_on(!p) || bug_on(!e)) |
| return -err_internal; |
| |
| conf = p->conf; |
| if (bug_on(!conf)) |
| return -err_internal; |
| |
| errcode = pt_enc_get_offset(e, &offset); |
| if (errcode < 0) |
| return report_lib_error(p, "error getting encoder offset", |
| errcode); |
| |
| begin = conf->begin; |
| end = conf->end; |
| pos = begin + offset; |
| if (pos < begin || end <= pos) |
| return err_internal; |
| |
| start = pos; |
| pos += size; |
| if (pos < begin || end <= pos) { |
| yasm_print_err(p->y, "buffer full", -err_no_mem); |
| return -err_no_mem; |
| } |
| |
| memcpy(start, buffer, size); |
| |
| errcode = pt_enc_sync_set(e, offset + size); |
| if (errcode < 0) |
| return report_lib_error(p, "error setting encoder offset", |
| errcode); |
| |
| p->pt_bytes_written += (int) size; |
| return (int) size; |
| } |
| |
| static int pt_raw_8(struct parser *p, struct pt_encoder *e, uint8_t value) |
| { |
| return pt_raw(p, e, &value, sizeof(value)); |
| } |
| |
| static int pt_raw_16(struct parser *p, struct pt_encoder *e, uint16_t value) |
| { |
| return pt_raw(p, e, &value, sizeof(value)); |
| } |
| |
| static int pt_raw_32(struct parser *p, struct pt_encoder *e, uint32_t value) |
| { |
| return pt_raw(p, e, &value, sizeof(value)); |
| } |
| |
| static int pt_raw_64(struct parser *p, struct pt_encoder *e, uint64_t value) |
| { |
| return pt_raw(p, e, &value, sizeof(value)); |
| } |
| |
| /* 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 if (strcmp(directive, "raw-8") == 0) { |
| uint8_t value; |
| |
| errcode = parse_uint8(&value, payload); |
| if (errcode < 0) { |
| yasm_print_err(p->y, payload, errcode); |
| return errcode; |
| } |
| |
| return pt_raw_8(p, e, value); |
| } else if (strcmp(directive, "raw-16") == 0) { |
| uint16_t value; |
| |
| errcode = parse_uint16(&value, payload); |
| if (errcode < 0) { |
| yasm_print_err(p->y, payload, errcode); |
| return errcode; |
| } |
| |
| return pt_raw_16(p, e, value); |
| } else if (strcmp(directive, "raw-32") == 0) { |
| uint32_t value; |
| |
| errcode = parse_uint32(&value, payload); |
| if (errcode < 0) { |
| yasm_print_err(p->y, payload, errcode); |
| return errcode; |
| } |
| |
| return pt_raw_32(p, e, value); |
| } else if (strcmp(directive, "raw-64") == 0) { |
| uint64_t value; |
| |
| errcode = parse_uint64(&value, payload); |
| if (errcode < 0) { |
| yasm_print_err(p->y, payload, errcode); |
| return errcode; |
| } |
| |
| return pt_raw_64(p, e, value); |
| } 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_t) 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) - 1); |
| |
| 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) - 1); |
| |
| 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", |
| (uint64_t) 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, |
| (uint64_t) 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, 1u, (size_t) 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; |
| } |