diff options
Diffstat (limited to 'tools/perf/util/header.c')
| -rw-r--r-- | tools/perf/util/header.c | 1233 |
1 files changed, 1185 insertions, 48 deletions
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index b6c1ad123ca9..bcd05d05b4f0 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | #define _FILE_OFFSET_BITS 64 | 1 | #define _FILE_OFFSET_BITS 64 |
| 2 | 2 | ||
| 3 | #include "util.h" | ||
| 3 | #include <sys/types.h> | 4 | #include <sys/types.h> |
| 4 | #include <byteswap.h> | 5 | #include <byteswap.h> |
| 5 | #include <unistd.h> | 6 | #include <unistd.h> |
| @@ -7,22 +8,29 @@ | |||
| 7 | #include <stdlib.h> | 8 | #include <stdlib.h> |
| 8 | #include <linux/list.h> | 9 | #include <linux/list.h> |
| 9 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
| 11 | #include <sys/utsname.h> | ||
| 10 | 12 | ||
| 11 | #include "evlist.h" | 13 | #include "evlist.h" |
| 12 | #include "evsel.h" | 14 | #include "evsel.h" |
| 13 | #include "util.h" | ||
| 14 | #include "header.h" | 15 | #include "header.h" |
| 15 | #include "../perf.h" | 16 | #include "../perf.h" |
| 16 | #include "trace-event.h" | 17 | #include "trace-event.h" |
| 17 | #include "session.h" | 18 | #include "session.h" |
| 18 | #include "symbol.h" | 19 | #include "symbol.h" |
| 19 | #include "debug.h" | 20 | #include "debug.h" |
| 21 | #include "cpumap.h" | ||
| 20 | 22 | ||
| 21 | static bool no_buildid_cache = false; | 23 | static bool no_buildid_cache = false; |
| 22 | 24 | ||
| 23 | static int event_count; | 25 | static int event_count; |
| 24 | static struct perf_trace_event_type *events; | 26 | static struct perf_trace_event_type *events; |
| 25 | 27 | ||
| 28 | static u32 header_argc; | ||
| 29 | static const char **header_argv; | ||
| 30 | |||
| 31 | static int dsos__write_buildid_table(struct perf_header *header, int fd); | ||
| 32 | static int perf_session__cache_build_ids(struct perf_session *session); | ||
| 33 | |||
| 26 | int perf_header__push_event(u64 id, const char *name) | 34 | int perf_header__push_event(u64 id, const char *name) |
| 27 | { | 35 | { |
| 28 | if (strlen(name) > MAX_EVENT_NAME) | 36 | if (strlen(name) > MAX_EVENT_NAME) |
| @@ -110,6 +118,1020 @@ static int write_padded(int fd, const void *bf, size_t count, | |||
| 110 | return err; | 118 | return err; |
| 111 | } | 119 | } |
| 112 | 120 | ||
| 121 | static int do_write_string(int fd, const char *str) | ||
| 122 | { | ||
| 123 | u32 len, olen; | ||
| 124 | int ret; | ||
| 125 | |||
| 126 | olen = strlen(str) + 1; | ||
| 127 | len = ALIGN(olen, NAME_ALIGN); | ||
| 128 | |||
| 129 | /* write len, incl. \0 */ | ||
| 130 | ret = do_write(fd, &len, sizeof(len)); | ||
| 131 | if (ret < 0) | ||
| 132 | return ret; | ||
| 133 | |||
| 134 | return write_padded(fd, str, olen, len); | ||
| 135 | } | ||
| 136 | |||
| 137 | static char *do_read_string(int fd, struct perf_header *ph) | ||
| 138 | { | ||
| 139 | ssize_t sz, ret; | ||
| 140 | u32 len; | ||
| 141 | char *buf; | ||
| 142 | |||
| 143 | sz = read(fd, &len, sizeof(len)); | ||
| 144 | if (sz < (ssize_t)sizeof(len)) | ||
| 145 | return NULL; | ||
| 146 | |||
| 147 | if (ph->needs_swap) | ||
| 148 | len = bswap_32(len); | ||
| 149 | |||
| 150 | buf = malloc(len); | ||
| 151 | if (!buf) | ||
| 152 | return NULL; | ||
| 153 | |||
| 154 | ret = read(fd, buf, len); | ||
| 155 | if (ret == (ssize_t)len) { | ||
| 156 | /* | ||
| 157 | * strings are padded by zeroes | ||
| 158 | * thus the actual strlen of buf | ||
| 159 | * may be less than len | ||
| 160 | */ | ||
| 161 | return buf; | ||
| 162 | } | ||
| 163 | |||
| 164 | free(buf); | ||
| 165 | return NULL; | ||
| 166 | } | ||
| 167 | |||
| 168 | int | ||
| 169 | perf_header__set_cmdline(int argc, const char **argv) | ||
| 170 | { | ||
| 171 | int i; | ||
| 172 | |||
| 173 | header_argc = (u32)argc; | ||
| 174 | |||
| 175 | /* do not include NULL termination */ | ||
| 176 | header_argv = calloc(argc, sizeof(char *)); | ||
| 177 | if (!header_argv) | ||
| 178 | return -ENOMEM; | ||
| 179 | |||
| 180 | /* | ||
| 181 | * must copy argv contents because it gets moved | ||
| 182 | * around during option parsing | ||
| 183 | */ | ||
| 184 | for (i = 0; i < argc ; i++) | ||
| 185 | header_argv[i] = argv[i]; | ||
| 186 | |||
| 187 | return 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | static int write_trace_info(int fd, struct perf_header *h __used, | ||
| 191 | struct perf_evlist *evlist) | ||
| 192 | { | ||
| 193 | return read_tracing_data(fd, &evlist->entries); | ||
| 194 | } | ||
| 195 | |||
| 196 | |||
| 197 | static int write_build_id(int fd, struct perf_header *h, | ||
| 198 | struct perf_evlist *evlist __used) | ||
| 199 | { | ||
| 200 | struct perf_session *session; | ||
| 201 | int err; | ||
| 202 | |||
| 203 | session = container_of(h, struct perf_session, header); | ||
| 204 | |||
| 205 | err = dsos__write_buildid_table(h, fd); | ||
| 206 | if (err < 0) { | ||
| 207 | pr_debug("failed to write buildid table\n"); | ||
| 208 | return err; | ||
| 209 | } | ||
| 210 | if (!no_buildid_cache) | ||
| 211 | perf_session__cache_build_ids(session); | ||
| 212 | |||
| 213 | return 0; | ||
| 214 | } | ||
| 215 | |||
| 216 | static int write_hostname(int fd, struct perf_header *h __used, | ||
| 217 | struct perf_evlist *evlist __used) | ||
| 218 | { | ||
| 219 | struct utsname uts; | ||
| 220 | int ret; | ||
| 221 | |||
| 222 | ret = uname(&uts); | ||
| 223 | if (ret < 0) | ||
| 224 | return -1; | ||
| 225 | |||
| 226 | return do_write_string(fd, uts.nodename); | ||
| 227 | } | ||
| 228 | |||
| 229 | static int write_osrelease(int fd, struct perf_header *h __used, | ||
| 230 | struct perf_evlist *evlist __used) | ||
| 231 | { | ||
| 232 | struct utsname uts; | ||
| 233 | int ret; | ||
| 234 | |||
| 235 | ret = uname(&uts); | ||
| 236 | if (ret < 0) | ||
| 237 | return -1; | ||
| 238 | |||
| 239 | return do_write_string(fd, uts.release); | ||
| 240 | } | ||
| 241 | |||
| 242 | static int write_arch(int fd, struct perf_header *h __used, | ||
| 243 | struct perf_evlist *evlist __used) | ||
| 244 | { | ||
| 245 | struct utsname uts; | ||
| 246 | int ret; | ||
| 247 | |||
| 248 | ret = uname(&uts); | ||
| 249 | if (ret < 0) | ||
| 250 | return -1; | ||
| 251 | |||
| 252 | return do_write_string(fd, uts.machine); | ||
| 253 | } | ||
| 254 | |||
| 255 | static int write_version(int fd, struct perf_header *h __used, | ||
| 256 | struct perf_evlist *evlist __used) | ||
| 257 | { | ||
| 258 | return do_write_string(fd, perf_version_string); | ||
| 259 | } | ||
| 260 | |||
| 261 | static int write_cpudesc(int fd, struct perf_header *h __used, | ||
| 262 | struct perf_evlist *evlist __used) | ||
| 263 | { | ||
| 264 | #ifndef CPUINFO_PROC | ||
| 265 | #define CPUINFO_PROC NULL | ||
| 266 | #endif | ||
| 267 | FILE *file; | ||
| 268 | char *buf = NULL; | ||
| 269 | char *s, *p; | ||
| 270 | const char *search = CPUINFO_PROC; | ||
| 271 | size_t len = 0; | ||
| 272 | int ret = -1; | ||
| 273 | |||
| 274 | if (!search) | ||
| 275 | return -1; | ||
| 276 | |||
| 277 | file = fopen("/proc/cpuinfo", "r"); | ||
| 278 | if (!file) | ||
| 279 | return -1; | ||
| 280 | |||
| 281 | while (getline(&buf, &len, file) > 0) { | ||
| 282 | ret = strncmp(buf, search, strlen(search)); | ||
| 283 | if (!ret) | ||
| 284 | break; | ||
| 285 | } | ||
| 286 | |||
| 287 | if (ret) | ||
| 288 | goto done; | ||
| 289 | |||
| 290 | s = buf; | ||
| 291 | |||
| 292 | p = strchr(buf, ':'); | ||
| 293 | if (p && *(p+1) == ' ' && *(p+2)) | ||
| 294 | s = p + 2; | ||
| 295 | p = strchr(s, '\n'); | ||
| 296 | if (p) | ||
| 297 | *p = '\0'; | ||
| 298 | |||
| 299 | /* squash extra space characters (branding string) */ | ||
| 300 | p = s; | ||
| 301 | while (*p) { | ||
| 302 | if (isspace(*p)) { | ||
| 303 | char *r = p + 1; | ||
| 304 | char *q = r; | ||
| 305 | *p = ' '; | ||
| 306 | while (*q && isspace(*q)) | ||
| 307 | q++; | ||
| 308 | if (q != (p+1)) | ||
| 309 | while ((*r++ = *q++)); | ||
| 310 | } | ||
| 311 | p++; | ||
| 312 | } | ||
| 313 | ret = do_write_string(fd, s); | ||
| 314 | done: | ||
| 315 | free(buf); | ||
| 316 | fclose(file); | ||
| 317 | return ret; | ||
| 318 | } | ||
| 319 | |||
| 320 | static int write_nrcpus(int fd, struct perf_header *h __used, | ||
| 321 | struct perf_evlist *evlist __used) | ||
| 322 | { | ||
| 323 | long nr; | ||
| 324 | u32 nrc, nra; | ||
| 325 | int ret; | ||
| 326 | |||
| 327 | nr = sysconf(_SC_NPROCESSORS_CONF); | ||
| 328 | if (nr < 0) | ||
| 329 | return -1; | ||
| 330 | |||
| 331 | nrc = (u32)(nr & UINT_MAX); | ||
| 332 | |||
| 333 | nr = sysconf(_SC_NPROCESSORS_ONLN); | ||
| 334 | if (nr < 0) | ||
| 335 | return -1; | ||
| 336 | |||
| 337 | nra = (u32)(nr & UINT_MAX); | ||
| 338 | |||
| 339 | ret = do_write(fd, &nrc, sizeof(nrc)); | ||
| 340 | if (ret < 0) | ||
| 341 | return ret; | ||
| 342 | |||
| 343 | return do_write(fd, &nra, sizeof(nra)); | ||
| 344 | } | ||
| 345 | |||
| 346 | static int write_event_desc(int fd, struct perf_header *h __used, | ||
| 347 | struct perf_evlist *evlist) | ||
| 348 | { | ||
| 349 | struct perf_evsel *attr; | ||
| 350 | u32 nre = 0, nri, sz; | ||
| 351 | int ret; | ||
| 352 | |||
| 353 | list_for_each_entry(attr, &evlist->entries, node) | ||
| 354 | nre++; | ||
| 355 | |||
| 356 | /* | ||
| 357 | * write number of events | ||
| 358 | */ | ||
| 359 | ret = do_write(fd, &nre, sizeof(nre)); | ||
| 360 | if (ret < 0) | ||
| 361 | return ret; | ||
| 362 | |||
| 363 | /* | ||
| 364 | * size of perf_event_attr struct | ||
| 365 | */ | ||
| 366 | sz = (u32)sizeof(attr->attr); | ||
| 367 | ret = do_write(fd, &sz, sizeof(sz)); | ||
| 368 | if (ret < 0) | ||
| 369 | return ret; | ||
| 370 | |||
| 371 | list_for_each_entry(attr, &evlist->entries, node) { | ||
| 372 | |||
| 373 | ret = do_write(fd, &attr->attr, sz); | ||
| 374 | if (ret < 0) | ||
| 375 | return ret; | ||
| 376 | /* | ||
| 377 | * write number of unique id per event | ||
| 378 | * there is one id per instance of an event | ||
| 379 | * | ||
| 380 | * copy into an nri to be independent of the | ||
| 381 | * type of ids, | ||
| 382 | */ | ||
| 383 | nri = attr->ids; | ||
| 384 | ret = do_write(fd, &nri, sizeof(nri)); | ||
| 385 | if (ret < 0) | ||
| 386 | return ret; | ||
| 387 | |||
| 388 | /* | ||
| 389 | * write event string as passed on cmdline | ||
| 390 | */ | ||
| 391 | ret = do_write_string(fd, attr->name); | ||
| 392 | if (ret < 0) | ||
| 393 | return ret; | ||
| 394 | /* | ||
| 395 | * write unique ids for this event | ||
| 396 | */ | ||
| 397 | ret = do_write(fd, attr->id, attr->ids * sizeof(u64)); | ||
| 398 | if (ret < 0) | ||
| 399 | return ret; | ||
| 400 | } | ||
| 401 | return 0; | ||
| 402 | } | ||
| 403 | |||
| 404 | static int write_cmdline(int fd, struct perf_header *h __used, | ||
| 405 | struct perf_evlist *evlist __used) | ||
| 406 | { | ||
| 407 | char buf[MAXPATHLEN]; | ||
| 408 | char proc[32]; | ||
| 409 | u32 i, n; | ||
| 410 | int ret; | ||
| 411 | |||
| 412 | /* | ||
| 413 | * actual atual path to perf binary | ||
| 414 | */ | ||
| 415 | sprintf(proc, "/proc/%d/exe", getpid()); | ||
| 416 | ret = readlink(proc, buf, sizeof(buf)); | ||
| 417 | if (ret <= 0) | ||
| 418 | return -1; | ||
| 419 | |||
| 420 | /* readlink() does not add null termination */ | ||
| 421 | buf[ret] = '\0'; | ||
| 422 | |||
| 423 | /* account for binary path */ | ||
| 424 | n = header_argc + 1; | ||
| 425 | |||
| 426 | ret = do_write(fd, &n, sizeof(n)); | ||
| 427 | if (ret < 0) | ||
| 428 | return ret; | ||
| 429 | |||
| 430 | ret = do_write_string(fd, buf); | ||
| 431 | if (ret < 0) | ||
| 432 | return ret; | ||
| 433 | |||
| 434 | for (i = 0 ; i < header_argc; i++) { | ||
| 435 | ret = do_write_string(fd, header_argv[i]); | ||
| 436 | if (ret < 0) | ||
| 437 | return ret; | ||
| 438 | } | ||
| 439 | return 0; | ||
| 440 | } | ||
| 441 | |||
| 442 | #define CORE_SIB_FMT \ | ||
| 443 | "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list" | ||
| 444 | #define THRD_SIB_FMT \ | ||
| 445 | "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list" | ||
| 446 | |||
| 447 | struct cpu_topo { | ||
| 448 | u32 core_sib; | ||
| 449 | u32 thread_sib; | ||
| 450 | char **core_siblings; | ||
| 451 | char **thread_siblings; | ||
| 452 | }; | ||
| 453 | |||
| 454 | static int build_cpu_topo(struct cpu_topo *tp, int cpu) | ||
| 455 | { | ||
| 456 | FILE *fp; | ||
| 457 | char filename[MAXPATHLEN]; | ||
| 458 | char *buf = NULL, *p; | ||
| 459 | size_t len = 0; | ||
| 460 | u32 i = 0; | ||
| 461 | int ret = -1; | ||
| 462 | |||
| 463 | sprintf(filename, CORE_SIB_FMT, cpu); | ||
| 464 | fp = fopen(filename, "r"); | ||
| 465 | if (!fp) | ||
| 466 | return -1; | ||
| 467 | |||
| 468 | if (getline(&buf, &len, fp) <= 0) | ||
| 469 | goto done; | ||
| 470 | |||
| 471 | fclose(fp); | ||
| 472 | |||
| 473 | p = strchr(buf, '\n'); | ||
| 474 | if (p) | ||
| 475 | *p = '\0'; | ||
| 476 | |||
| 477 | for (i = 0; i < tp->core_sib; i++) { | ||
| 478 | if (!strcmp(buf, tp->core_siblings[i])) | ||
| 479 | break; | ||
| 480 | } | ||
| 481 | if (i == tp->core_sib) { | ||
| 482 | tp->core_siblings[i] = buf; | ||
| 483 | tp->core_sib++; | ||
| 484 | buf = NULL; | ||
| 485 | len = 0; | ||
| 486 | } | ||
| 487 | |||
| 488 | sprintf(filename, THRD_SIB_FMT, cpu); | ||
| 489 | fp = fopen(filename, "r"); | ||
| 490 | if (!fp) | ||
| 491 | goto done; | ||
| 492 | |||
| 493 | if (getline(&buf, &len, fp) <= 0) | ||
| 494 | goto done; | ||
| 495 | |||
| 496 | p = strchr(buf, '\n'); | ||
| 497 | if (p) | ||
| 498 | *p = '\0'; | ||
| 499 | |||
| 500 | for (i = 0; i < tp->thread_sib; i++) { | ||
| 501 | if (!strcmp(buf, tp->thread_siblings[i])) | ||
| 502 | break; | ||
| 503 | } | ||
| 504 | if (i == tp->thread_sib) { | ||
| 505 | tp->thread_siblings[i] = buf; | ||
| 506 | tp->thread_sib++; | ||
| 507 | buf = NULL; | ||
| 508 | } | ||
| 509 | ret = 0; | ||
| 510 | done: | ||
| 511 | if(fp) | ||
| 512 | fclose(fp); | ||
| 513 | free(buf); | ||
| 514 | return ret; | ||
| 515 | } | ||
| 516 | |||
| 517 | static void free_cpu_topo(struct cpu_topo *tp) | ||
| 518 | { | ||
| 519 | u32 i; | ||
| 520 | |||
| 521 | if (!tp) | ||
| 522 | return; | ||
| 523 | |||
| 524 | for (i = 0 ; i < tp->core_sib; i++) | ||
| 525 | free(tp->core_siblings[i]); | ||
| 526 | |||
| 527 | for (i = 0 ; i < tp->thread_sib; i++) | ||
| 528 | free(tp->thread_siblings[i]); | ||
| 529 | |||
| 530 | free(tp); | ||
| 531 | } | ||
| 532 | |||
| 533 | static struct cpu_topo *build_cpu_topology(void) | ||
| 534 | { | ||
| 535 | struct cpu_topo *tp; | ||
| 536 | void *addr; | ||
| 537 | u32 nr, i; | ||
| 538 | size_t sz; | ||
| 539 | long ncpus; | ||
| 540 | int ret = -1; | ||
| 541 | |||
| 542 | ncpus = sysconf(_SC_NPROCESSORS_CONF); | ||
| 543 | if (ncpus < 0) | ||
| 544 | return NULL; | ||
| 545 | |||
| 546 | nr = (u32)(ncpus & UINT_MAX); | ||
| 547 | |||
| 548 | sz = nr * sizeof(char *); | ||
| 549 | |||
| 550 | addr = calloc(1, sizeof(*tp) + 2 * sz); | ||
| 551 | if (!addr) | ||
| 552 | return NULL; | ||
| 553 | |||
| 554 | tp = addr; | ||
| 555 | |||
| 556 | addr += sizeof(*tp); | ||
| 557 | tp->core_siblings = addr; | ||
| 558 | addr += sz; | ||
| 559 | tp->thread_siblings = addr; | ||
| 560 | |||
| 561 | for (i = 0; i < nr; i++) { | ||
| 562 | ret = build_cpu_topo(tp, i); | ||
| 563 | if (ret < 0) | ||
| 564 | break; | ||
| 565 | } | ||
| 566 | if (ret) { | ||
| 567 | free_cpu_topo(tp); | ||
| 568 | tp = NULL; | ||
| 569 | } | ||
| 570 | return tp; | ||
| 571 | } | ||
| 572 | |||
| 573 | static int write_cpu_topology(int fd, struct perf_header *h __used, | ||
| 574 | struct perf_evlist *evlist __used) | ||
| 575 | { | ||
| 576 | struct cpu_topo *tp; | ||
| 577 | u32 i; | ||
| 578 | int ret; | ||
| 579 | |||
| 580 | tp = build_cpu_topology(); | ||
| 581 | if (!tp) | ||
| 582 | return -1; | ||
| 583 | |||
| 584 | ret = do_write(fd, &tp->core_sib, sizeof(tp->core_sib)); | ||
| 585 | if (ret < 0) | ||
| 586 | goto done; | ||
| 587 | |||
| 588 | for (i = 0; i < tp->core_sib; i++) { | ||
| 589 | ret = do_write_string(fd, tp->core_siblings[i]); | ||
| 590 | if (ret < 0) | ||
| 591 | goto done; | ||
| 592 | } | ||
| 593 | ret = do_write(fd, &tp->thread_sib, sizeof(tp->thread_sib)); | ||
| 594 | if (ret < 0) | ||
| 595 | goto done; | ||
| 596 | |||
| 597 | for (i = 0; i < tp->thread_sib; i++) { | ||
| 598 | ret = do_write_string(fd, tp->thread_siblings[i]); | ||
| 599 | if (ret < 0) | ||
| 600 | break; | ||
| 601 | } | ||
| 602 | done: | ||
| 603 | free_cpu_topo(tp); | ||
| 604 | return ret; | ||
| 605 | } | ||
| 606 | |||
| 607 | |||
| 608 | |||
| 609 | static int write_total_mem(int fd, struct perf_header *h __used, | ||
| 610 | struct perf_evlist *evlist __used) | ||
| 611 | { | ||
| 612 | char *buf = NULL; | ||
| 613 | FILE *fp; | ||
| 614 | size_t len = 0; | ||
| 615 | int ret = -1, n; | ||
| 616 | uint64_t mem; | ||
| 617 | |||
| 618 | fp = fopen("/proc/meminfo", "r"); | ||
| 619 | if (!fp) | ||
| 620 | return -1; | ||
| 621 | |||
| 622 | while (getline(&buf, &len, fp) > 0) { | ||
| 623 | ret = strncmp(buf, "MemTotal:", 9); | ||
| 624 | if (!ret) | ||
| 625 | break; | ||
| 626 | } | ||
| 627 | if (!ret) { | ||
| 628 | n = sscanf(buf, "%*s %"PRIu64, &mem); | ||
| 629 | if (n == 1) | ||
| 630 | ret = do_write(fd, &mem, sizeof(mem)); | ||
| 631 | } | ||
| 632 | free(buf); | ||
| 633 | fclose(fp); | ||
| 634 | return ret; | ||
| 635 | } | ||
| 636 | |||
| 637 | static int write_topo_node(int fd, int node) | ||
| 638 | { | ||
| 639 | char str[MAXPATHLEN]; | ||
| 640 | char field[32]; | ||
| 641 | char *buf = NULL, *p; | ||
| 642 | size_t len = 0; | ||
| 643 | FILE *fp; | ||
| 644 | u64 mem_total, mem_free, mem; | ||
| 645 | int ret = -1; | ||
| 646 | |||
| 647 | sprintf(str, "/sys/devices/system/node/node%d/meminfo", node); | ||
| 648 | fp = fopen(str, "r"); | ||
| 649 | if (!fp) | ||
| 650 | return -1; | ||
| 651 | |||
| 652 | while (getline(&buf, &len, fp) > 0) { | ||
| 653 | /* skip over invalid lines */ | ||
| 654 | if (!strchr(buf, ':')) | ||
| 655 | continue; | ||
| 656 | if (sscanf(buf, "%*s %*d %s %"PRIu64, field, &mem) != 2) | ||
| 657 | goto done; | ||
| 658 | if (!strcmp(field, "MemTotal:")) | ||
| 659 | mem_total = mem; | ||
| 660 | if (!strcmp(field, "MemFree:")) | ||
| 661 | mem_free = mem; | ||
| 662 | } | ||
| 663 | |||
| 664 | fclose(fp); | ||
| 665 | |||
| 666 | ret = do_write(fd, &mem_total, sizeof(u64)); | ||
| 667 | if (ret) | ||
| 668 | goto done; | ||
| 669 | |||
| 670 | ret = do_write(fd, &mem_free, sizeof(u64)); | ||
| 671 | if (ret) | ||
| 672 | goto done; | ||
| 673 | |||
| 674 | ret = -1; | ||
| 675 | sprintf(str, "/sys/devices/system/node/node%d/cpulist", node); | ||
| 676 | |||
| 677 | fp = fopen(str, "r"); | ||
| 678 | if (!fp) | ||
| 679 | goto done; | ||
| 680 | |||
| 681 | if (getline(&buf, &len, fp) <= 0) | ||
| 682 | goto done; | ||
| 683 | |||
| 684 | p = strchr(buf, '\n'); | ||
| 685 | if (p) | ||
| 686 | *p = '\0'; | ||
| 687 | |||
| 688 | ret = do_write_string(fd, buf); | ||
| 689 | done: | ||
| 690 | free(buf); | ||
| 691 | fclose(fp); | ||
| 692 | return ret; | ||
| 693 | } | ||
| 694 | |||
| 695 | static int write_numa_topology(int fd, struct perf_header *h __used, | ||
| 696 | struct perf_evlist *evlist __used) | ||
| 697 | { | ||
| 698 | char *buf = NULL; | ||
| 699 | size_t len = 0; | ||
| 700 | FILE *fp; | ||
| 701 | struct cpu_map *node_map = NULL; | ||
| 702 | char *c; | ||
| 703 | u32 nr, i, j; | ||
| 704 | int ret = -1; | ||
| 705 | |||
| 706 | fp = fopen("/sys/devices/system/node/online", "r"); | ||
| 707 | if (!fp) | ||
| 708 | return -1; | ||
| 709 | |||
| 710 | if (getline(&buf, &len, fp) <= 0) | ||
| 711 | goto done; | ||
| 712 | |||
| 713 | c = strchr(buf, '\n'); | ||
| 714 | if (c) | ||
| 715 | *c = '\0'; | ||
| 716 | |||
| 717 | node_map = cpu_map__new(buf); | ||
| 718 | if (!node_map) | ||
| 719 | goto done; | ||
| 720 | |||
| 721 | nr = (u32)node_map->nr; | ||
| 722 | |||
| 723 | ret = do_write(fd, &nr, sizeof(nr)); | ||
| 724 | if (ret < 0) | ||
| 725 | goto done; | ||
| 726 | |||
| 727 | for (i = 0; i < nr; i++) { | ||
| 728 | j = (u32)node_map->map[i]; | ||
| 729 | ret = do_write(fd, &j, sizeof(j)); | ||
| 730 | if (ret < 0) | ||
| 731 | break; | ||
| 732 | |||
| 733 | ret = write_topo_node(fd, i); | ||
| 734 | if (ret < 0) | ||
| 735 | break; | ||
| 736 | } | ||
| 737 | done: | ||
| 738 | free(buf); | ||
| 739 | fclose(fp); | ||
| 740 | free(node_map); | ||
| 741 | return ret; | ||
| 742 | } | ||
| 743 | |||
| 744 | /* | ||
| 745 | * default get_cpuid(): nothing gets recorded | ||
| 746 | * actual implementation must be in arch/$(ARCH)/util/header.c | ||
| 747 | */ | ||
| 748 | int __attribute__((weak)) get_cpuid(char *buffer __used, size_t sz __used) | ||
| 749 | { | ||
| 750 | return -1; | ||
| 751 | } | ||
| 752 | |||
| 753 | static int write_cpuid(int fd, struct perf_header *h __used, | ||
| 754 | struct perf_evlist *evlist __used) | ||
| 755 | { | ||
| 756 | char buffer[64]; | ||
| 757 | int ret; | ||
| 758 | |||
| 759 | ret = get_cpuid(buffer, sizeof(buffer)); | ||
| 760 | if (!ret) | ||
| 761 | goto write_it; | ||
| 762 | |||
| 763 | return -1; | ||
| 764 | write_it: | ||
| 765 | return do_write_string(fd, buffer); | ||
| 766 | } | ||
| 767 | |||
| 768 | static void print_hostname(struct perf_header *ph, int fd, FILE *fp) | ||
| 769 | { | ||
| 770 | char *str = do_read_string(fd, ph); | ||
| 771 | fprintf(fp, "# hostname : %s\n", str); | ||
| 772 | free(str); | ||
| 773 | } | ||
| 774 | |||
| 775 | static void print_osrelease(struct perf_header *ph, int fd, FILE *fp) | ||
| 776 | { | ||
| 777 | char *str = do_read_string(fd, ph); | ||
| 778 | fprintf(fp, "# os release : %s\n", str); | ||
| 779 | free(str); | ||
| 780 | } | ||
| 781 | |||
| 782 | static void print_arch(struct perf_header *ph, int fd, FILE *fp) | ||
| 783 | { | ||
| 784 | char *str = do_read_string(fd, ph); | ||
| 785 | fprintf(fp, "# arch : %s\n", str); | ||
| 786 | free(str); | ||
| 787 | } | ||
| 788 | |||
| 789 | static void print_cpudesc(struct perf_header *ph, int fd, FILE *fp) | ||
| 790 | { | ||
| 791 | char *str = do_read_string(fd, ph); | ||
| 792 | fprintf(fp, "# cpudesc : %s\n", str); | ||
| 793 | free(str); | ||
| 794 | } | ||
| 795 | |||
| 796 | static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp) | ||
| 797 | { | ||
| 798 | ssize_t ret; | ||
| 799 | u32 nr; | ||
| 800 | |||
| 801 | ret = read(fd, &nr, sizeof(nr)); | ||
| 802 | if (ret != (ssize_t)sizeof(nr)) | ||
| 803 | nr = -1; /* interpreted as error */ | ||
| 804 | |||
| 805 | if (ph->needs_swap) | ||
| 806 | nr = bswap_32(nr); | ||
| 807 | |||
| 808 | fprintf(fp, "# nrcpus online : %u\n", nr); | ||
| 809 | |||
| 810 | ret = read(fd, &nr, sizeof(nr)); | ||
| 811 | if (ret != (ssize_t)sizeof(nr)) | ||
| 812 | nr = -1; /* interpreted as error */ | ||
| 813 | |||
| 814 | if (ph->needs_swap) | ||
| 815 | nr = bswap_32(nr); | ||
| 816 | |||
| 817 | fprintf(fp, "# nrcpus avail : %u\n", nr); | ||
| 818 | } | ||
| 819 | |||
| 820 | static void print_version(struct perf_header *ph, int fd, FILE *fp) | ||
| 821 | { | ||
| 822 | char *str = do_read_string(fd, ph); | ||
| 823 | fprintf(fp, "# perf version : %s\n", str); | ||
| 824 | free(str); | ||
| 825 | } | ||
| 826 | |||
| 827 | static void print_cmdline(struct perf_header *ph, int fd, FILE *fp) | ||
| 828 | { | ||
| 829 | ssize_t ret; | ||
| 830 | char *str; | ||
| 831 | u32 nr, i; | ||
| 832 | |||
| 833 | ret = read(fd, &nr, sizeof(nr)); | ||
| 834 | if (ret != (ssize_t)sizeof(nr)) | ||
| 835 | return; | ||
| 836 | |||
| 837 | if (ph->needs_swap) | ||
| 838 | nr = bswap_32(nr); | ||
| 839 | |||
| 840 | fprintf(fp, "# cmdline : "); | ||
| 841 | |||
| 842 | for (i = 0; i < nr; i++) { | ||
| 843 | str = do_read_string(fd, ph); | ||
| 844 | fprintf(fp, "%s ", str); | ||
| 845 | free(str); | ||
| 846 | } | ||
| 847 | fputc('\n', fp); | ||
| 848 | } | ||
| 849 | |||
| 850 | static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp) | ||
| 851 | { | ||
| 852 | ssize_t ret; | ||
| 853 | u32 nr, i; | ||
| 854 | char *str; | ||
| 855 | |||
| 856 | ret = read(fd, &nr, sizeof(nr)); | ||
| 857 | if (ret != (ssize_t)sizeof(nr)) | ||
| 858 | return; | ||
| 859 | |||
| 860 | if (ph->needs_swap) | ||
| 861 | nr = bswap_32(nr); | ||
| 862 | |||
| 863 | for (i = 0; i < nr; i++) { | ||
| 864 | str = do_read_string(fd, ph); | ||
| 865 | fprintf(fp, "# sibling cores : %s\n", str); | ||
| 866 | free(str); | ||
| 867 | } | ||
| 868 | |||
| 869 | ret = read(fd, &nr, sizeof(nr)); | ||
| 870 | if (ret != (ssize_t)sizeof(nr)) | ||
| 871 | return; | ||
| 872 | |||
| 873 | if (ph->needs_swap) | ||
| 874 | nr = bswap_32(nr); | ||
| 875 | |||
| 876 | for (i = 0; i < nr; i++) { | ||
| 877 | str = do_read_string(fd, ph); | ||
| 878 | fprintf(fp, "# sibling threads : %s\n", str); | ||
| 879 | free(str); | ||
| 880 | } | ||
| 881 | } | ||
| 882 | |||
| 883 | static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | ||
| 884 | { | ||
| 885 | struct perf_event_attr attr; | ||
| 886 | uint64_t id; | ||
| 887 | void *buf = NULL; | ||
| 888 | char *str; | ||
| 889 | u32 nre, sz, nr, i, j, msz; | ||
| 890 | int ret; | ||
| 891 | |||
| 892 | /* number of events */ | ||
| 893 | ret = read(fd, &nre, sizeof(nre)); | ||
| 894 | if (ret != (ssize_t)sizeof(nre)) | ||
| 895 | goto error; | ||
| 896 | |||
| 897 | if (ph->needs_swap) | ||
| 898 | nre = bswap_32(nre); | ||
| 899 | |||
| 900 | ret = read(fd, &sz, sizeof(sz)); | ||
| 901 | if (ret != (ssize_t)sizeof(sz)) | ||
| 902 | goto error; | ||
| 903 | |||
| 904 | if (ph->needs_swap) | ||
| 905 | sz = bswap_32(sz); | ||
| 906 | |||
| 907 | /* | ||
| 908 | * ensure it is at least to our ABI rev | ||
| 909 | */ | ||
| 910 | if (sz < (u32)sizeof(attr)) | ||
| 911 | goto error; | ||
| 912 | |||
| 913 | memset(&attr, 0, sizeof(attr)); | ||
| 914 | |||
| 915 | /* read entire region to sync up to next field */ | ||
| 916 | buf = malloc(sz); | ||
| 917 | if (!buf) | ||
| 918 | goto error; | ||
| 919 | |||
| 920 | msz = sizeof(attr); | ||
| 921 | if (sz < msz) | ||
| 922 | msz = sz; | ||
| 923 | |||
| 924 | for (i = 0 ; i < nre; i++) { | ||
| 925 | |||
| 926 | ret = read(fd, buf, sz); | ||
| 927 | if (ret != (ssize_t)sz) | ||
| 928 | goto error; | ||
| 929 | |||
| 930 | if (ph->needs_swap) | ||
| 931 | perf_event__attr_swap(buf); | ||
| 932 | |||
| 933 | memcpy(&attr, buf, msz); | ||
| 934 | |||
| 935 | ret = read(fd, &nr, sizeof(nr)); | ||
| 936 | if (ret != (ssize_t)sizeof(nr)) | ||
| 937 | goto error; | ||
| 938 | |||
| 939 | if (ph->needs_swap) | ||
| 940 | nr = bswap_32(nr); | ||
| 941 | |||
| 942 | str = do_read_string(fd, ph); | ||
| 943 | fprintf(fp, "# event : name = %s, ", str); | ||
| 944 | free(str); | ||
| 945 | |||
| 946 | fprintf(fp, "type = %d, config = 0x%"PRIx64 | ||
| 947 | ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64, | ||
| 948 | attr.type, | ||
| 949 | (u64)attr.config, | ||
| 950 | (u64)attr.config1, | ||
| 951 | (u64)attr.config2); | ||
| 952 | |||
| 953 | fprintf(fp, ", excl_usr = %d, excl_kern = %d", | ||
| 954 | attr.exclude_user, | ||
| 955 | attr.exclude_kernel); | ||
| 956 | |||
| 957 | if (nr) | ||
| 958 | fprintf(fp, ", id = {"); | ||
| 959 | |||
| 960 | for (j = 0 ; j < nr; j++) { | ||
| 961 | ret = read(fd, &id, sizeof(id)); | ||
| 962 | if (ret != (ssize_t)sizeof(id)) | ||
| 963 | goto error; | ||
| 964 | |||
| 965 | if (ph->needs_swap) | ||
| 966 | id = bswap_64(id); | ||
| 967 | |||
| 968 | if (j) | ||
| 969 | fputc(',', fp); | ||
| 970 | |||
| 971 | fprintf(fp, " %"PRIu64, id); | ||
| 972 | } | ||
| 973 | if (nr && j == nr) | ||
| 974 | fprintf(fp, " }"); | ||
| 975 | fputc('\n', fp); | ||
| 976 | } | ||
| 977 | free(buf); | ||
| 978 | return; | ||
| 979 | error: | ||
| 980 | fprintf(fp, "# event desc: not available or unable to read\n"); | ||
| 981 | } | ||
| 982 | |||
| 983 | static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp) | ||
| 984 | { | ||
| 985 | uint64_t mem; | ||
| 986 | ssize_t ret; | ||
| 987 | |||
| 988 | ret = read(fd, &mem, sizeof(mem)); | ||
| 989 | if (ret != sizeof(mem)) | ||
| 990 | goto error; | ||
| 991 | |||
| 992 | if (h->needs_swap) | ||
| 993 | mem = bswap_64(mem); | ||
| 994 | |||
| 995 | fprintf(fp, "# total memory : %"PRIu64" kB\n", mem); | ||
| 996 | return; | ||
| 997 | error: | ||
| 998 | fprintf(fp, "# total memory : unknown\n"); | ||
| 999 | } | ||
| 1000 | |||
| 1001 | static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp) | ||
| 1002 | { | ||
| 1003 | ssize_t ret; | ||
| 1004 | u32 nr, c, i; | ||
| 1005 | char *str; | ||
| 1006 | uint64_t mem_total, mem_free; | ||
| 1007 | |||
| 1008 | /* nr nodes */ | ||
| 1009 | ret = read(fd, &nr, sizeof(nr)); | ||
| 1010 | if (ret != (ssize_t)sizeof(nr)) | ||
| 1011 | goto error; | ||
| 1012 | |||
| 1013 | if (h->needs_swap) | ||
| 1014 | nr = bswap_32(nr); | ||
| 1015 | |||
| 1016 | for (i = 0; i < nr; i++) { | ||
| 1017 | |||
| 1018 | /* node number */ | ||
| 1019 | ret = read(fd, &c, sizeof(c)); | ||
| 1020 | if (ret != (ssize_t)sizeof(c)) | ||
| 1021 | goto error; | ||
| 1022 | |||
| 1023 | if (h->needs_swap) | ||
| 1024 | c = bswap_32(c); | ||
| 1025 | |||
| 1026 | ret = read(fd, &mem_total, sizeof(u64)); | ||
| 1027 | if (ret != sizeof(u64)) | ||
| 1028 | goto error; | ||
| 1029 | |||
| 1030 | ret = read(fd, &mem_free, sizeof(u64)); | ||
| 1031 | if (ret != sizeof(u64)) | ||
| 1032 | goto error; | ||
| 1033 | |||
| 1034 | if (h->needs_swap) { | ||
| 1035 | mem_total = bswap_64(mem_total); | ||
| 1036 | mem_free = bswap_64(mem_free); | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | fprintf(fp, "# node%u meminfo : total = %"PRIu64" kB," | ||
| 1040 | " free = %"PRIu64" kB\n", | ||
| 1041 | c, | ||
| 1042 | mem_total, | ||
| 1043 | mem_free); | ||
| 1044 | |||
| 1045 | str = do_read_string(fd, h); | ||
| 1046 | fprintf(fp, "# node%u cpu list : %s\n", c, str); | ||
| 1047 | free(str); | ||
| 1048 | } | ||
| 1049 | return; | ||
| 1050 | error: | ||
| 1051 | fprintf(fp, "# numa topology : not available\n"); | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | static void print_cpuid(struct perf_header *ph, int fd, FILE *fp) | ||
| 1055 | { | ||
| 1056 | char *str = do_read_string(fd, ph); | ||
| 1057 | fprintf(fp, "# cpuid : %s\n", str); | ||
| 1058 | free(str); | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | struct feature_ops { | ||
| 1062 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | ||
| 1063 | void (*print)(struct perf_header *h, int fd, FILE *fp); | ||
| 1064 | const char *name; | ||
| 1065 | bool full_only; | ||
| 1066 | }; | ||
| 1067 | |||
| 1068 | #define FEAT_OPA(n, w, p) \ | ||
| 1069 | [n] = { .name = #n, .write = w, .print = p } | ||
| 1070 | #define FEAT_OPF(n, w, p) \ | ||
| 1071 | [n] = { .name = #n, .write = w, .print = p, .full_only = true } | ||
| 1072 | |||
| 1073 | static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | ||
| 1074 | FEAT_OPA(HEADER_TRACE_INFO, write_trace_info, NULL), | ||
| 1075 | FEAT_OPA(HEADER_BUILD_ID, write_build_id, NULL), | ||
| 1076 | FEAT_OPA(HEADER_HOSTNAME, write_hostname, print_hostname), | ||
| 1077 | FEAT_OPA(HEADER_OSRELEASE, write_osrelease, print_osrelease), | ||
| 1078 | FEAT_OPA(HEADER_VERSION, write_version, print_version), | ||
| 1079 | FEAT_OPA(HEADER_ARCH, write_arch, print_arch), | ||
| 1080 | FEAT_OPA(HEADER_NRCPUS, write_nrcpus, print_nrcpus), | ||
| 1081 | FEAT_OPA(HEADER_CPUDESC, write_cpudesc, print_cpudesc), | ||
| 1082 | FEAT_OPA(HEADER_CPUID, write_cpuid, print_cpuid), | ||
| 1083 | FEAT_OPA(HEADER_TOTAL_MEM, write_total_mem, print_total_mem), | ||
| 1084 | FEAT_OPA(HEADER_EVENT_DESC, write_event_desc, print_event_desc), | ||
| 1085 | FEAT_OPA(HEADER_CMDLINE, write_cmdline, print_cmdline), | ||
| 1086 | FEAT_OPF(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology), | ||
| 1087 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology), | ||
| 1088 | }; | ||
| 1089 | |||
| 1090 | struct header_print_data { | ||
| 1091 | FILE *fp; | ||
| 1092 | bool full; /* extended list of headers */ | ||
| 1093 | }; | ||
| 1094 | |||
| 1095 | static int perf_file_section__fprintf_info(struct perf_file_section *section, | ||
| 1096 | struct perf_header *ph, | ||
| 1097 | int feat, int fd, void *data) | ||
| 1098 | { | ||
| 1099 | struct header_print_data *hd = data; | ||
| 1100 | |||
| 1101 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { | ||
| 1102 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " | ||
| 1103 | "%d, continuing...\n", section->offset, feat); | ||
| 1104 | return 0; | ||
| 1105 | } | ||
| 1106 | if (feat < HEADER_TRACE_INFO || feat >= HEADER_LAST_FEATURE) { | ||
| 1107 | pr_warning("unknown feature %d\n", feat); | ||
| 1108 | return -1; | ||
| 1109 | } | ||
| 1110 | if (!feat_ops[feat].print) | ||
| 1111 | return 0; | ||
| 1112 | |||
| 1113 | if (!feat_ops[feat].full_only || hd->full) | ||
| 1114 | feat_ops[feat].print(ph, fd, hd->fp); | ||
| 1115 | else | ||
| 1116 | fprintf(hd->fp, "# %s info available, use -I to display\n", | ||
| 1117 | feat_ops[feat].name); | ||
| 1118 | |||
| 1119 | return 0; | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) | ||
| 1123 | { | ||
| 1124 | struct header_print_data hd; | ||
| 1125 | struct perf_header *header = &session->header; | ||
| 1126 | int fd = session->fd; | ||
| 1127 | hd.fp = fp; | ||
| 1128 | hd.full = full; | ||
| 1129 | |||
| 1130 | perf_header__process_sections(header, fd, &hd, | ||
| 1131 | perf_file_section__fprintf_info); | ||
| 1132 | return 0; | ||
| 1133 | } | ||
| 1134 | |||
| 113 | #define dsos__for_each_with_build_id(pos, head) \ | 1135 | #define dsos__for_each_with_build_id(pos, head) \ |
| 114 | list_for_each_entry(pos, head, node) \ | 1136 | list_for_each_entry(pos, head, node) \ |
| 115 | if (!pos->has_build_id) \ | 1137 | if (!pos->has_build_id) \ |
| @@ -267,7 +1289,7 @@ int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | |||
| 267 | if (access(linkname, F_OK)) | 1289 | if (access(linkname, F_OK)) |
| 268 | goto out_free; | 1290 | goto out_free; |
| 269 | 1291 | ||
| 270 | if (readlink(linkname, filename, size) < 0) | 1292 | if (readlink(linkname, filename, size - 1) < 0) |
| 271 | goto out_free; | 1293 | goto out_free; |
| 272 | 1294 | ||
| 273 | if (unlink(linkname)) | 1295 | if (unlink(linkname)) |
| @@ -356,15 +1378,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with | |||
| 356 | return ret; | 1378 | return ret; |
| 357 | } | 1379 | } |
| 358 | 1380 | ||
| 1381 | static int do_write_feat(int fd, struct perf_header *h, int type, | ||
| 1382 | struct perf_file_section **p, | ||
| 1383 | struct perf_evlist *evlist) | ||
| 1384 | { | ||
| 1385 | int err; | ||
| 1386 | int ret = 0; | ||
| 1387 | |||
| 1388 | if (perf_header__has_feat(h, type)) { | ||
| 1389 | |||
| 1390 | (*p)->offset = lseek(fd, 0, SEEK_CUR); | ||
| 1391 | |||
| 1392 | err = feat_ops[type].write(fd, h, evlist); | ||
| 1393 | if (err < 0) { | ||
| 1394 | pr_debug("failed to write feature %d\n", type); | ||
| 1395 | |||
| 1396 | /* undo anything written */ | ||
| 1397 | lseek(fd, (*p)->offset, SEEK_SET); | ||
| 1398 | |||
| 1399 | return -1; | ||
| 1400 | } | ||
| 1401 | (*p)->size = lseek(fd, 0, SEEK_CUR) - (*p)->offset; | ||
| 1402 | (*p)++; | ||
| 1403 | } | ||
| 1404 | return ret; | ||
| 1405 | } | ||
| 1406 | |||
| 359 | static int perf_header__adds_write(struct perf_header *header, | 1407 | static int perf_header__adds_write(struct perf_header *header, |
| 360 | struct perf_evlist *evlist, int fd) | 1408 | struct perf_evlist *evlist, int fd) |
| 361 | { | 1409 | { |
| 362 | int nr_sections; | 1410 | int nr_sections; |
| 363 | struct perf_session *session; | 1411 | struct perf_session *session; |
| 364 | struct perf_file_section *feat_sec; | 1412 | struct perf_file_section *feat_sec, *p; |
| 365 | int sec_size; | 1413 | int sec_size; |
| 366 | u64 sec_start; | 1414 | u64 sec_start; |
| 367 | int idx = 0, err; | 1415 | int err; |
| 368 | 1416 | ||
| 369 | session = container_of(header, struct perf_session, header); | 1417 | session = container_of(header, struct perf_session, header); |
| 370 | 1418 | ||
| @@ -376,7 +1424,7 @@ static int perf_header__adds_write(struct perf_header *header, | |||
| 376 | if (!nr_sections) | 1424 | if (!nr_sections) |
| 377 | return 0; | 1425 | return 0; |
| 378 | 1426 | ||
| 379 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | 1427 | feat_sec = p = calloc(sizeof(*feat_sec), nr_sections); |
| 380 | if (feat_sec == NULL) | 1428 | if (feat_sec == NULL) |
| 381 | return -ENOMEM; | 1429 | return -ENOMEM; |
| 382 | 1430 | ||
| @@ -385,36 +1433,69 @@ static int perf_header__adds_write(struct perf_header *header, | |||
| 385 | sec_start = header->data_offset + header->data_size; | 1433 | sec_start = header->data_offset + header->data_size; |
| 386 | lseek(fd, sec_start + sec_size, SEEK_SET); | 1434 | lseek(fd, sec_start + sec_size, SEEK_SET); |
| 387 | 1435 | ||
| 388 | if (perf_header__has_feat(header, HEADER_TRACE_INFO)) { | 1436 | err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist); |
| 389 | struct perf_file_section *trace_sec; | 1437 | if (err) |
| 390 | 1438 | goto out_free; | |
| 391 | trace_sec = &feat_sec[idx++]; | ||
| 392 | 1439 | ||
| 393 | /* Write trace info */ | 1440 | err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist); |
| 394 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | 1441 | if (err) { |
| 395 | read_tracing_data(fd, &evlist->entries); | 1442 | perf_header__clear_feat(header, HEADER_BUILD_ID); |
| 396 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | 1443 | goto out_free; |
| 397 | } | 1444 | } |
| 398 | 1445 | ||
| 399 | if (perf_header__has_feat(header, HEADER_BUILD_ID)) { | 1446 | err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist); |
| 400 | struct perf_file_section *buildid_sec; | 1447 | if (err) |
| 1448 | perf_header__clear_feat(header, HEADER_HOSTNAME); | ||
| 401 | 1449 | ||
| 402 | buildid_sec = &feat_sec[idx++]; | 1450 | err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist); |
| 1451 | if (err) | ||
| 1452 | perf_header__clear_feat(header, HEADER_OSRELEASE); | ||
| 403 | 1453 | ||
| 404 | /* Write build-ids */ | 1454 | err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist); |
| 405 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | 1455 | if (err) |
| 406 | err = dsos__write_buildid_table(header, fd); | 1456 | perf_header__clear_feat(header, HEADER_VERSION); |
| 407 | if (err < 0) { | 1457 | |
| 408 | pr_debug("failed to write buildid table\n"); | 1458 | err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist); |
| 409 | goto out_free; | 1459 | if (err) |
| 410 | } | 1460 | perf_header__clear_feat(header, HEADER_ARCH); |
| 411 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - | 1461 | |
| 412 | buildid_sec->offset; | 1462 | err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist); |
| 413 | if (!no_buildid_cache) | 1463 | if (err) |
| 414 | perf_session__cache_build_ids(session); | 1464 | perf_header__clear_feat(header, HEADER_NRCPUS); |
| 415 | } | 1465 | |
| 1466 | err = do_write_feat(fd, header, HEADER_CPUDESC, &p, evlist); | ||
| 1467 | if (err) | ||
| 1468 | perf_header__clear_feat(header, HEADER_CPUDESC); | ||
| 1469 | |||
| 1470 | err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist); | ||
| 1471 | if (err) | ||
| 1472 | perf_header__clear_feat(header, HEADER_CPUID); | ||
| 1473 | |||
| 1474 | err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist); | ||
| 1475 | if (err) | ||
| 1476 | perf_header__clear_feat(header, HEADER_TOTAL_MEM); | ||
| 1477 | |||
| 1478 | err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist); | ||
| 1479 | if (err) | ||
| 1480 | perf_header__clear_feat(header, HEADER_CMDLINE); | ||
| 1481 | |||
| 1482 | err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist); | ||
| 1483 | if (err) | ||
| 1484 | perf_header__clear_feat(header, HEADER_EVENT_DESC); | ||
| 1485 | |||
| 1486 | err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist); | ||
| 1487 | if (err) | ||
| 1488 | perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY); | ||
| 1489 | |||
| 1490 | err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist); | ||
| 1491 | if (err) | ||
| 1492 | perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY); | ||
| 416 | 1493 | ||
| 417 | lseek(fd, sec_start, SEEK_SET); | 1494 | lseek(fd, sec_start, SEEK_SET); |
| 1495 | /* | ||
| 1496 | * may write more than needed due to dropped feature, but | ||
| 1497 | * this is okay, reader will skip the mising entries | ||
| 1498 | */ | ||
| 418 | err = do_write(fd, feat_sec, sec_size); | 1499 | err = do_write(fd, feat_sec, sec_size); |
| 419 | if (err < 0) | 1500 | if (err < 0) |
| 420 | pr_debug("failed to write feature section\n"); | 1501 | pr_debug("failed to write feature section\n"); |
| @@ -554,9 +1635,10 @@ static int perf_header__getbuffer64(struct perf_header *header, | |||
| 554 | } | 1635 | } |
| 555 | 1636 | ||
| 556 | int perf_header__process_sections(struct perf_header *header, int fd, | 1637 | int perf_header__process_sections(struct perf_header *header, int fd, |
| 1638 | void *data, | ||
| 557 | int (*process)(struct perf_file_section *section, | 1639 | int (*process)(struct perf_file_section *section, |
| 558 | struct perf_header *ph, | 1640 | struct perf_header *ph, |
| 559 | int feat, int fd)) | 1641 | int feat, int fd, void *data)) |
| 560 | { | 1642 | { |
| 561 | struct perf_file_section *feat_sec; | 1643 | struct perf_file_section *feat_sec; |
| 562 | int nr_sections; | 1644 | int nr_sections; |
| @@ -584,7 +1666,7 @@ int perf_header__process_sections(struct perf_header *header, int fd, | |||
| 584 | if (perf_header__has_feat(header, feat)) { | 1666 | if (perf_header__has_feat(header, feat)) { |
| 585 | struct perf_file_section *sec = &feat_sec[idx++]; | 1667 | struct perf_file_section *sec = &feat_sec[idx++]; |
| 586 | 1668 | ||
| 587 | err = process(sec, header, feat, fd); | 1669 | err = process(sec, header, feat, fd, data); |
| 588 | if (err < 0) | 1670 | if (err < 0) |
| 589 | break; | 1671 | break; |
| 590 | } | 1672 | } |
| @@ -621,21 +1703,41 @@ int perf_file_header__read(struct perf_file_header *header, | |||
| 621 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); | 1703 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); |
| 622 | else | 1704 | else |
| 623 | return -1; | 1705 | return -1; |
| 1706 | } else if (ph->needs_swap) { | ||
| 1707 | unsigned int i; | ||
| 1708 | /* | ||
| 1709 | * feature bitmap is declared as an array of unsigned longs -- | ||
| 1710 | * not good since its size can differ between the host that | ||
| 1711 | * generated the data file and the host analyzing the file. | ||
| 1712 | * | ||
| 1713 | * We need to handle endianness, but we don't know the size of | ||
| 1714 | * the unsigned long where the file was generated. Take a best | ||
| 1715 | * guess at determining it: try 64-bit swap first (ie., file | ||
| 1716 | * created on a 64-bit host), and check if the hostname feature | ||
| 1717 | * bit is set (this feature bit is forced on as of fbe96f2). | ||
| 1718 | * If the bit is not, undo the 64-bit swap and try a 32-bit | ||
| 1719 | * swap. If the hostname bit is still not set (e.g., older data | ||
| 1720 | * file), punt and fallback to the original behavior -- | ||
| 1721 | * clearing all feature bits and setting buildid. | ||
| 1722 | */ | ||
| 1723 | for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) | ||
| 1724 | header->adds_features[i] = bswap_64(header->adds_features[i]); | ||
| 1725 | |||
| 1726 | if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { | ||
| 1727 | for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) { | ||
| 1728 | header->adds_features[i] = bswap_64(header->adds_features[i]); | ||
| 1729 | header->adds_features[i] = bswap_32(header->adds_features[i]); | ||
| 1730 | } | ||
| 1731 | } | ||
| 1732 | |||
| 1733 | if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { | ||
| 1734 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); | ||
| 1735 | set_bit(HEADER_BUILD_ID, header->adds_features); | ||
| 1736 | } | ||
| 624 | } | 1737 | } |
| 625 | 1738 | ||
| 626 | memcpy(&ph->adds_features, &header->adds_features, | 1739 | memcpy(&ph->adds_features, &header->adds_features, |
| 627 | sizeof(ph->adds_features)); | 1740 | sizeof(ph->adds_features)); |
| 628 | /* | ||
| 629 | * FIXME: hack that assumes that if we need swap the perf.data file | ||
| 630 | * may be coming from an arch with a different word-size, ergo different | ||
| 631 | * DEFINE_BITMAP format, investigate more later, but for now its mostly | ||
| 632 | * safe to assume that we have a build-id section. Trace files probably | ||
| 633 | * have several other issues in this realm anyway... | ||
| 634 | */ | ||
| 635 | if (ph->needs_swap) { | ||
| 636 | memset(&ph->adds_features, 0, sizeof(ph->adds_features)); | ||
| 637 | perf_header__set_feat(ph, HEADER_BUILD_ID); | ||
| 638 | } | ||
| 639 | 1741 | ||
| 640 | ph->event_offset = header->event_types.offset; | 1742 | ph->event_offset = header->event_types.offset; |
| 641 | ph->event_size = header->event_types.size; | 1743 | ph->event_size = header->event_types.size; |
| @@ -796,7 +1898,7 @@ out: | |||
| 796 | 1898 | ||
| 797 | static int perf_file_section__process(struct perf_file_section *section, | 1899 | static int perf_file_section__process(struct perf_file_section *section, |
| 798 | struct perf_header *ph, | 1900 | struct perf_header *ph, |
| 799 | int feat, int fd) | 1901 | int feat, int fd, void *data __used) |
| 800 | { | 1902 | { |
| 801 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { | 1903 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { |
| 802 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " | 1904 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " |
| @@ -813,6 +1915,21 @@ static int perf_file_section__process(struct perf_file_section *section, | |||
| 813 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) | 1915 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) |
| 814 | pr_debug("Failed to read buildids, continuing...\n"); | 1916 | pr_debug("Failed to read buildids, continuing...\n"); |
| 815 | break; | 1917 | break; |
| 1918 | |||
| 1919 | case HEADER_HOSTNAME: | ||
| 1920 | case HEADER_OSRELEASE: | ||
| 1921 | case HEADER_VERSION: | ||
| 1922 | case HEADER_ARCH: | ||
| 1923 | case HEADER_NRCPUS: | ||
| 1924 | case HEADER_CPUDESC: | ||
| 1925 | case HEADER_CPUID: | ||
| 1926 | case HEADER_TOTAL_MEM: | ||
| 1927 | case HEADER_CMDLINE: | ||
| 1928 | case HEADER_EVENT_DESC: | ||
| 1929 | case HEADER_CPU_TOPOLOGY: | ||
| 1930 | case HEADER_NUMA_TOPOLOGY: | ||
| 1931 | break; | ||
| 1932 | |||
| 816 | default: | 1933 | default: |
| 817 | pr_debug("unknown feature %d, continuing...\n", feat); | 1934 | pr_debug("unknown feature %d, continuing...\n", feat); |
| 818 | } | 1935 | } |
| @@ -935,7 +2052,8 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
| 935 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 2052 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
| 936 | } | 2053 | } |
| 937 | 2054 | ||
| 938 | perf_header__process_sections(header, fd, perf_file_section__process); | 2055 | perf_header__process_sections(header, fd, NULL, |
| 2056 | perf_file_section__process); | ||
| 939 | 2057 | ||
| 940 | lseek(fd, header->data_offset, SEEK_SET); | 2058 | lseek(fd, header->data_offset, SEEK_SET); |
| 941 | 2059 | ||
| @@ -1100,15 +2218,29 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, | |||
| 1100 | struct perf_session *session __unused) | 2218 | struct perf_session *session __unused) |
| 1101 | { | 2219 | { |
| 1102 | union perf_event ev; | 2220 | union perf_event ev; |
| 2221 | struct tracing_data *tdata; | ||
| 1103 | ssize_t size = 0, aligned_size = 0, padding; | 2222 | ssize_t size = 0, aligned_size = 0, padding; |
| 1104 | int err __used = 0; | 2223 | int err __used = 0; |
| 1105 | 2224 | ||
| 2225 | /* | ||
| 2226 | * We are going to store the size of the data followed | ||
| 2227 | * by the data contents. Since the fd descriptor is a pipe, | ||
| 2228 | * we cannot seek back to store the size of the data once | ||
| 2229 | * we know it. Instead we: | ||
| 2230 | * | ||
| 2231 | * - write the tracing data to the temp file | ||
| 2232 | * - get/write the data size to pipe | ||
| 2233 | * - write the tracing data from the temp file | ||
| 2234 | * to the pipe | ||
| 2235 | */ | ||
| 2236 | tdata = tracing_data_get(&evlist->entries, fd, true); | ||
| 2237 | if (!tdata) | ||
| 2238 | return -1; | ||
| 2239 | |||
| 1106 | memset(&ev, 0, sizeof(ev)); | 2240 | memset(&ev, 0, sizeof(ev)); |
| 1107 | 2241 | ||
| 1108 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; | 2242 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; |
| 1109 | size = read_tracing_data_size(fd, &evlist->entries); | 2243 | size = tdata->size; |
| 1110 | if (size <= 0) | ||
| 1111 | return size; | ||
| 1112 | aligned_size = ALIGN(size, sizeof(u64)); | 2244 | aligned_size = ALIGN(size, sizeof(u64)); |
| 1113 | padding = aligned_size - size; | 2245 | padding = aligned_size - size; |
| 1114 | ev.tracing_data.header.size = sizeof(ev.tracing_data); | 2246 | ev.tracing_data.header.size = sizeof(ev.tracing_data); |
| @@ -1116,7 +2248,12 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, | |||
| 1116 | 2248 | ||
| 1117 | process(&ev, NULL, session); | 2249 | process(&ev, NULL, session); |
| 1118 | 2250 | ||
| 1119 | err = read_tracing_data(fd, &evlist->entries); | 2251 | /* |
| 2252 | * The put function will copy all the tracing data | ||
| 2253 | * stored in temp file to the pipe. | ||
| 2254 | */ | ||
| 2255 | tracing_data_put(tdata); | ||
| 2256 | |||
| 1120 | write_padded(fd, NULL, 0, padding); | 2257 | write_padded(fd, NULL, 0, padding); |
| 1121 | 2258 | ||
| 1122 | return aligned_size; | 2259 | return aligned_size; |
