|
| 1 | +/* |
| 2 | + * Copyright (C) 2018 Intel Corporation. All rights reserved. |
| 3 | + * |
| 4 | + * Redistribution and use in source and binary forms, with or without |
| 5 | + * modification, are permitted provided that the following conditions |
| 6 | + * are met: |
| 7 | + * |
| 8 | + * * Redistributions of source code must retain the above copyright |
| 9 | + * notice, this list of conditions and the following disclaimer. |
| 10 | + * * Redistributions in binary form must reproduce the above copyright |
| 11 | + * notice, this list of conditions and the following disclaimer in |
| 12 | + * the documentation and/or other materials provided with the |
| 13 | + * distribution. |
| 14 | + * * Neither the name of Intel Corporation nor the names of its |
| 15 | + * contributors may be used to endorse or promote products derived |
| 16 | + * from this software without specific prior written permission. |
| 17 | + * |
| 18 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 | + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 | + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 | + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 | + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 | + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 | + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 | + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 | + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 | + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 | + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 | + */ |
| 30 | + |
| 31 | +#include <stdio.h> |
| 32 | +#include <stdlib.h> |
| 33 | +#include <unistd.h> |
| 34 | +#include <fcntl.h> |
| 35 | +#include <sys/mman.h> |
| 36 | +#include <time.h> |
| 37 | +#include <signal.h> |
| 38 | +#include <errno.h> |
| 39 | +#include <pthread.h> |
| 40 | +#include <string.h> |
| 41 | + |
| 42 | +#include "acrntrace.h" |
| 43 | +#include "trace_event.h" |
| 44 | + |
| 45 | +/* for opt */ |
| 46 | +static uint64_t period = 10000; |
| 47 | +static const char optString[] = "t:hc"; |
| 48 | +static const char dev_name[] = "/dev/acrn_trace"; |
| 49 | + |
| 50 | +static uint32_t flags; |
| 51 | +static char trace_file_dir[TRACE_FILE_DIR_LEN]; |
| 52 | + |
| 53 | +static reader_struct *reader; |
| 54 | +static int pcpu_num = 0; |
| 55 | + |
| 56 | +static void display_usage(void) |
| 57 | +{ |
| 58 | + printf("acrntrace - tool to collect ACRN trace data\n" |
| 59 | + "[Usage] acrntrace [-t] [period in msec] [-ch]\n\n" |
| 60 | + "[Options]\n" |
| 61 | + "\t-h: print this message\n" |
| 62 | + "\t-t: period_in_ms: specify polling interval [1-999]\n" |
| 63 | + "\t-c: clear the buffered old data\n"); |
| 64 | +} |
| 65 | + |
| 66 | +static int parse_opt(int argc, char *argv[]) |
| 67 | +{ |
| 68 | + int opt, ret; |
| 69 | + |
| 70 | + while ((opt = getopt(argc, argv, optString)) != -1) { |
| 71 | + switch (opt) { |
| 72 | + case 't': |
| 73 | + ret = atoi(optarg); |
| 74 | + if (ret <= 0 || ret >=1000) { |
| 75 | + pr_err("'-t' require integer between [1-999]\n"); |
| 76 | + return -EINVAL; |
| 77 | + } |
| 78 | + period = ret * 1000; |
| 79 | + pr_dbg("Period is %lu\n", period); |
| 80 | + break; |
| 81 | + case 'c': |
| 82 | + flags |= FLAG_CLEAR_BUF; |
| 83 | + break; |
| 84 | + case 'h': |
| 85 | + display_usage(); |
| 86 | + return -EINVAL; |
| 87 | + default: |
| 88 | + /* Undefined operation. */ |
| 89 | + display_usage(); |
| 90 | + return -EINVAL; |
| 91 | + } |
| 92 | + }; |
| 93 | + return 0; |
| 94 | +} |
| 95 | + |
| 96 | +static int shell_cmd(const char *cmd, char *outbuf, int len) |
| 97 | +{ |
| 98 | + FILE *ptr; |
| 99 | + char cmd_buf[256]; |
| 100 | + int ret; |
| 101 | + |
| 102 | + if (!outbuf) |
| 103 | + return system(cmd); |
| 104 | + |
| 105 | + memset(cmd_buf, 0, sizeof(cmd_buf)); |
| 106 | + memset(outbuf, 0, len); |
| 107 | + snprintf(cmd_buf, sizeof(cmd_buf), "%s 2>&1", cmd); |
| 108 | + ptr = popen(cmd_buf, "re"); |
| 109 | + if (!ptr) |
| 110 | + return -1; |
| 111 | + |
| 112 | + ret = fread(outbuf, 1, len, ptr); |
| 113 | + pclose(ptr); |
| 114 | + |
| 115 | + return ret; |
| 116 | +} |
| 117 | + |
| 118 | +static int get_cpu_num(void) |
| 119 | +{ |
| 120 | + |
| 121 | + char cmd[128]; |
| 122 | + char buf[16]; |
| 123 | + int ret; |
| 124 | + |
| 125 | + snprintf(cmd, sizeof(cmd), "ls %s_* | wc -l", dev_name); |
| 126 | + |
| 127 | + ret = shell_cmd(cmd, buf, sizeof(buf)); |
| 128 | + if (ret <= 0) { |
| 129 | + pr_err("Faile to get cpu number, use default 4\n"); |
| 130 | + return PCPU_NUM; |
| 131 | + } |
| 132 | + |
| 133 | + ret = atoi(buf); |
| 134 | + if (ret <= 0) { |
| 135 | + pr_err("Wrong cpu number, use default 4\n"); |
| 136 | + return PCPU_NUM; |
| 137 | + } |
| 138 | + |
| 139 | + return ret; |
| 140 | +} |
| 141 | + |
| 142 | +static double get_cpu_freq(void) |
| 143 | +{ |
| 144 | + char cmd[] = |
| 145 | + "cat /proc/cpuinfo | grep -m 1 \"cpu MHz\" | awk '{print $4}'"; |
| 146 | + char buf[16]; |
| 147 | + int ret; |
| 148 | + double freq = 0; |
| 149 | + |
| 150 | + ret = shell_cmd(cmd, buf, sizeof(buf)); |
| 151 | + |
| 152 | + if (ret <= 0) { |
| 153 | + pr_err("Faile to get cpu freq, use default 1920MHz\n"); |
| 154 | + return 1920.00; |
| 155 | + } |
| 156 | + |
| 157 | + freq = atof(buf); |
| 158 | + if (freq <= 0) { |
| 159 | + pr_err("Invalid cpu freq string, use default 1920MHz\n"); |
| 160 | + return 1920.00; |
| 161 | + } |
| 162 | + |
| 163 | + return freq; |
| 164 | +} |
| 165 | + |
| 166 | +static int create_trace_file_dir(char *dir) |
| 167 | +{ |
| 168 | + int status; |
| 169 | + char cmd[CMD_MAX_LEN]; |
| 170 | + char time_str[TIME_STR_LEN]; |
| 171 | + time_t timep; |
| 172 | + struct tm *p; |
| 173 | + |
| 174 | + time(&timep); |
| 175 | + p = localtime(&timep); |
| 176 | + snprintf(time_str, TIME_STR_LEN, "%d%02d%02d-%02d%02d%02d", |
| 177 | + (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday, |
| 178 | + p->tm_hour, p->tm_min, p->tm_sec); |
| 179 | + pr_info("start tracing at %s\n", time_str); |
| 180 | + |
| 181 | + snprintf(dir, TRACE_FILE_DIR_LEN, "%s%s", TRACE_FILE_ROOT, time_str); |
| 182 | + |
| 183 | + memset(cmd, 0, CMD_MAX_LEN); |
| 184 | + snprintf(cmd, CMD_MAX_LEN, "%s %s", "mkdir -p ", dir); |
| 185 | + |
| 186 | + status = system(cmd); |
| 187 | + if (-1 == status) |
| 188 | + return -1; /* failed to execute sh */ |
| 189 | + |
| 190 | + pr_dbg("dir %s creted\n", dir); |
| 191 | + |
| 192 | + return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; |
| 193 | +} |
| 194 | + |
| 195 | +/* function executed in each consumer thread */ |
| 196 | +static void reader_fn(param_t * param) |
| 197 | +{ |
| 198 | + int ret; |
| 199 | + uint32_t cpuid = param->cpuid; |
| 200 | + FILE *fp = param->trace_filep; |
| 201 | + shared_buf_t *sbuf = param->sbuf; |
| 202 | + trace_ev_t e; |
| 203 | + |
| 204 | + pr_dbg("reader thread[%lu] created for FILE*[0x%p]\n", |
| 205 | + pthread_self(), fp); |
| 206 | + |
| 207 | + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); |
| 208 | + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); |
| 209 | + |
| 210 | + /* Clear the old data in sbuf */ |
| 211 | + if (flags & FLAG_CLEAR_BUF) |
| 212 | + sbuf_clear_buffered(sbuf); |
| 213 | + |
| 214 | + /* write cpu freq to the first line of output file */ |
| 215 | + fprintf(fp, "CPU Freq: %f\n", get_cpu_freq()); |
| 216 | + |
| 217 | + while (1) { |
| 218 | + do { |
| 219 | + ret = sbuf_get(sbuf, (void *)&e); |
| 220 | + if (ret == 0) |
| 221 | + break; |
| 222 | + else if (ret < 0) { |
| 223 | + pr_err("sbuf[%u] read error: %d\n", cpuid, ret); |
| 224 | + return; |
| 225 | + } |
| 226 | + |
| 227 | + fprintf(fp, "%u | %lu | ", cpuid, e.tsc); |
| 228 | + switch (e.id) { |
| 229 | + /* defined in trace_event.h */ |
| 230 | + /* for each ev type */ |
| 231 | + ALL_CASES; |
| 232 | + } |
| 233 | + } while (ret > 0); |
| 234 | + |
| 235 | + usleep(period); |
| 236 | + } |
| 237 | +} |
| 238 | + |
| 239 | +static int create_reader(reader_struct * reader, uint32_t cpu) |
| 240 | +{ |
| 241 | + char trace_file_name[TRACE_FILE_NAME_LEN]; |
| 242 | + |
| 243 | + snprintf(reader->dev_name, DEV_PATH_LEN, "%s_%u", dev_name, cpu); |
| 244 | + reader->param.cpuid = cpu; |
| 245 | + |
| 246 | + reader->dev_fd = open(reader->dev_name, O_RDWR); |
| 247 | + if (reader->dev_fd < 0) { |
| 248 | + pr_err("Failed to open %s, err %d\n", reader->dev_name, errno); |
| 249 | + reader->dev_fd = 0; |
| 250 | + return -1; |
| 251 | + } |
| 252 | + |
| 253 | + reader->param.sbuf = mmap(NULL, MMAP_SIZE, |
| 254 | + PROT_READ | PROT_WRITE, |
| 255 | + MAP_SHARED, reader->dev_fd, 0); |
| 256 | + if (reader->param.sbuf == MAP_FAILED) { |
| 257 | + pr_err("mmap failed for cpu%d, errno %d\n", cpu, errno); |
| 258 | + reader->param.sbuf = NULL; |
| 259 | + return -2; |
| 260 | + } |
| 261 | + |
| 262 | + pr_dbg("sbuf[%d]:\nmagic_num: %lx\nele_num: %u\n ele_size: %u\n", |
| 263 | + cpu, reader->param.sbuf->magic, reader->param.sbuf->ele_num, |
| 264 | + reader->param.sbuf->ele_size); |
| 265 | + |
| 266 | + snprintf(trace_file_name, TRACE_FILE_NAME_LEN, "%s/%d", trace_file_dir, |
| 267 | + cpu); |
| 268 | + reader->param.trace_filep = fopen(trace_file_name, "w+"); |
| 269 | + if (!reader->param.trace_filep) { |
| 270 | + pr_err("Failed to open %s, err %d\n", trace_file_name, errno); |
| 271 | + return -3; |
| 272 | + } |
| 273 | + |
| 274 | + pr_info("trace data file %s created for %s\n", |
| 275 | + trace_file_name, reader->dev_name); |
| 276 | + |
| 277 | + if (pthread_create(&reader->thrd, NULL, |
| 278 | + (void *)&reader_fn, &reader->param)) { |
| 279 | + pr_err("failed to create reader thread, %d\n", cpu); |
| 280 | + return -4; |
| 281 | + } |
| 282 | + |
| 283 | + return 0; |
| 284 | +} |
| 285 | + |
| 286 | +static void destory_reader(reader_struct * reader) |
| 287 | +{ |
| 288 | + if (reader->thrd) { |
| 289 | + pthread_cancel(reader->thrd); |
| 290 | + if (pthread_join(reader->thrd, NULL) != 0) |
| 291 | + pr_err("failed to cancel thread[%lu]\n", reader->thrd); |
| 292 | + else |
| 293 | + reader->thrd = 0; |
| 294 | + } |
| 295 | + |
| 296 | + if (reader->param.sbuf) { |
| 297 | + munmap(reader->param.sbuf, MMAP_SIZE); |
| 298 | + reader->param.sbuf = NULL; |
| 299 | + } |
| 300 | + |
| 301 | + if (reader->dev_fd) { |
| 302 | + close(reader->dev_fd); |
| 303 | + reader->dev_fd = 0; |
| 304 | + } |
| 305 | + |
| 306 | + if (reader->param.trace_filep) { |
| 307 | + fflush(reader->param.trace_filep); |
| 308 | + fclose(reader->param.trace_filep); |
| 309 | + reader->param.trace_filep = NULL; |
| 310 | + } |
| 311 | +} |
| 312 | + |
| 313 | +static void handle_on_exit(void) |
| 314 | +{ |
| 315 | + uint32_t cpu; |
| 316 | + |
| 317 | + /* if nothing to release */ |
| 318 | + if (!(flags & FLAG_TO_REL)) |
| 319 | + return; |
| 320 | + |
| 321 | + pr_info("exiting - to release resources...\n"); |
| 322 | + |
| 323 | + foreach_cpu(cpu) |
| 324 | + destory_reader(&reader[cpu]); |
| 325 | +} |
| 326 | + |
| 327 | +static void signal_exit_handler(int sig) |
| 328 | +{ |
| 329 | + pr_info("exit on signal %d\n", sig); |
| 330 | + exit(0); |
| 331 | +} |
| 332 | + |
| 333 | +int main(int argc, char *argv[]) |
| 334 | +{ |
| 335 | + uint32_t cpu = 0; |
| 336 | + |
| 337 | + /* parse options */ |
| 338 | + if (parse_opt(argc, argv)) |
| 339 | + exit(EXIT_FAILURE); |
| 340 | + |
| 341 | + /* how many cpus */ |
| 342 | + pcpu_num = get_cpu_num(); |
| 343 | + reader = malloc(sizeof(reader_struct) * pcpu_num); |
| 344 | + if (!reader) { |
| 345 | + pr_err("Failed to allocate reader memory\n"); |
| 346 | + exit(EXIT_FAILURE); |
| 347 | + } |
| 348 | + memset(reader, 0, sizeof(reader_struct) * pcpu_num); |
| 349 | + |
| 350 | + /* create dir for trace file */ |
| 351 | + if (create_trace_file_dir(trace_file_dir)) { |
| 352 | + pr_err("Failed to create dir for trace files\n"); |
| 353 | + exit(EXIT_FAILURE); |
| 354 | + } |
| 355 | + |
| 356 | + atexit(handle_on_exit); |
| 357 | + |
| 358 | + /* acquair res for each trace dev */ |
| 359 | + flags |= FLAG_TO_REL; |
| 360 | + foreach_cpu(cpu) |
| 361 | + if (create_reader(&reader[cpu], cpu) < 0) |
| 362 | + goto out_free; |
| 363 | + |
| 364 | + /* for kill exit handling */ |
| 365 | + signal(SIGTERM, signal_exit_handler); |
| 366 | + signal(SIGINT, signal_exit_handler); |
| 367 | + |
| 368 | + /* wait for user input to stop */ |
| 369 | + printf("q <enter> to quit:\n"); |
| 370 | + while (getchar() != 'q') |
| 371 | + printf("q <enter> to quit:\n"); |
| 372 | + |
| 373 | + out_free: |
| 374 | + foreach_cpu(cpu) |
| 375 | + destory_reader(&reader[cpu]); |
| 376 | + |
| 377 | + free(reader); |
| 378 | + flags &= ~FLAG_TO_REL; |
| 379 | + |
| 380 | + return EXIT_SUCCESS; |
| 381 | +} |
0 commit comments