lcc/clcc/lib/helpers/trace_helpers.c (319 lines of code) (raw):

/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ // Copyright (c) 2020 Wenbo Zhang // // Based on ksyms improvements from Andrii Nakryiko, add more helpers. // 28-Feb-2020 Wenbo Zhang Created this. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/resource.h> #include <time.h> #include "trace_helpers.h" #define min(x, y) ({ \ typeof(x) _min1 = (x); \ typeof(y) _min2 = (y); \ (void) (&_min1 == &_min2); \ _min1 < _min2 ? _min1 : _min2; }) #define DISK_NAME_LEN 32 #define MINORBITS 20 #define MINORMASK ((1U << MINORBITS) - 1) #define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi)) struct ksyms { struct ksym *syms; int syms_sz; int syms_cap; char *strs; int strs_sz; int strs_cap; }; static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, unsigned long addr) { size_t new_cap, name_len = strlen(name) + 1; struct ksym *ksym; void *tmp; if (ksyms->strs_sz + name_len > ksyms->strs_cap) { new_cap = ksyms->strs_cap * 4 / 3; if (new_cap < ksyms->strs_sz + name_len) new_cap = ksyms->strs_sz + name_len; if (new_cap < 1024) new_cap = 1024; tmp = realloc(ksyms->strs, new_cap); if (!tmp) return -1; ksyms->strs = tmp; ksyms->strs_cap = new_cap; } if (ksyms->syms_sz + 1 > ksyms->syms_cap) { new_cap = ksyms->syms_cap * 4 / 3; if (new_cap < 1024) new_cap = 1024; tmp = realloc(ksyms->syms, sizeof(*ksyms->syms) * new_cap); if (!tmp) return -1; ksyms->syms = tmp; ksyms->syms_cap = new_cap; } ksym = &ksyms->syms[ksyms->syms_sz]; /* while constructing, re-use pointer as just a plain offset */ ksym->name = (void *)(unsigned long)ksyms->strs_sz; ksym->addr = addr; memcpy(ksyms->strs + ksyms->strs_sz, name, name_len); ksyms->strs_sz += name_len; ksyms->syms_sz++; return 0; } static int ksym_cmp(const void *p1, const void *p2) { const struct ksym *s1 = p1, *s2 = p2; if (s1->addr == s2->addr) return strcmp(s1->name, s2->name); return s1->addr < s2->addr ? -1 : 1; } struct ksyms *ksyms__load(void) { char sym_type, sym_name[256]; struct ksyms *ksyms; unsigned long sym_addr; int i, ret; FILE *f; f = fopen("/proc/kallsyms", "r"); if (!f) return NULL; ksyms = calloc(1, sizeof(*ksyms)); if (!ksyms) goto err_out; while (true) { ret = fscanf(f, "%lx %c %s%*[^\n]\n", &sym_addr, &sym_type, sym_name); if (ret == EOF && feof(f)) break; if (ret != 3) goto err_out; if (ksyms__add_symbol(ksyms, sym_name, sym_addr)) goto err_out; } /* now when strings are finalized, adjust pointers properly */ for (i = 0; i < ksyms->syms_sz; i++) ksyms->syms[i].name += (unsigned long)ksyms->strs; qsort(ksyms->syms, ksyms->syms_sz, sizeof(*ksyms->syms), ksym_cmp); fclose(f); return ksyms; err_out: ksyms__free(ksyms); fclose(f); return NULL; } void ksyms__free(struct ksyms *ksyms) { if (!ksyms) return; free(ksyms->syms); free(ksyms->strs); free(ksyms); } const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, unsigned long addr) { int start = 0, end = ksyms->syms_sz - 1, mid; unsigned long sym_addr; /* find largest sym_addr <= addr using binary search */ while (start < end) { mid = start + (end - start + 1) / 2; sym_addr = ksyms->syms[mid].addr; if (sym_addr <= addr) start = mid; else end = mid - 1; } if (start == end && ksyms->syms[start].addr <= addr) return &ksyms->syms[start]; return NULL; } const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, const char *name) { int i; for (i = 0; i < ksyms->syms_sz; i++) { if (strcmp(ksyms->syms[i].name, name) == 0) return &ksyms->syms[i]; } return NULL; } struct partitions { struct partition *items; int sz; }; static int partitions__add_partition(struct partitions *partitions, const char *name, unsigned int dev) { struct partition *partition; void *tmp; tmp = realloc(partitions->items, (partitions->sz + 1) * sizeof(*partitions->items)); if (!tmp) return -1; partitions->items = tmp; partition = &partitions->items[partitions->sz]; partition->name = strdup(name); partition->dev = dev; partitions->sz++; return 0; } struct partitions *partitions__load(void) { char part_name[DISK_NAME_LEN]; unsigned int devmaj, devmin; unsigned long long nop; struct partitions *partitions; char buf[64]; FILE *f; f = fopen("/proc/partitions", "r"); if (!f) return NULL; partitions = calloc(1, sizeof(*partitions)); if (!partitions) goto err_out; while (fgets(buf, sizeof(buf), f) != NULL) { /* skip heading */ if (buf[0] != ' ' || buf[0] == '\n') continue; if (sscanf(buf, "%u %u %llu %s", &devmaj, &devmin, &nop, part_name) != 4) goto err_out; if (partitions__add_partition(partitions, part_name, MKDEV(devmaj, devmin))) goto err_out; } fclose(f); return partitions; err_out: partitions__free(partitions); fclose(f); return NULL; } void partitions__free(struct partitions *partitions) { int i; if (!partitions) return; for (i = 0; i < partitions->sz; i++) free(partitions->items[i].name); free(partitions->items); free(partitions); } const struct partition * partitions__get_by_dev(const struct partitions *partitions, unsigned int dev) { int i; for (i = 0; i < partitions->sz; i++) { if (partitions->items[i].dev == dev) return &partitions->items[i]; } return NULL; } const struct partition * partitions__get_by_name(const struct partitions *partitions, const char *name) { int i; for (i = 0; i < partitions->sz; i++) { if (strcmp(partitions->items[i].name, name) == 0) return &partitions->items[i]; } return NULL; } static void print_stars(unsigned int val, unsigned int val_max, int width) { int num_stars, num_spaces, i; bool need_plus; num_stars = min(val, val_max) * width / val_max; num_spaces = width - num_stars; need_plus = val > val_max; for (i = 0; i < num_stars; i++) printf("*"); for (i = 0; i < num_spaces; i++) printf(" "); if (need_plus) printf("+"); } void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type) { int stars_max = 40, idx_max = -1; unsigned int val, val_max = 0; unsigned long long low, high; int stars, width, i; for (i = 0; i < vals_size; i++) { val = vals[i]; if (val > 0) idx_max = i; if (val > val_max) val_max = val; } if (idx_max < 0) return; printf("%*s%-*s : count distribution\n", idx_max <= 32 ? 5 : 15, "", idx_max <= 32 ? 19 : 29, val_type); if (idx_max <= 32) stars = stars_max; else stars = stars_max / 2; for (i = 0; i <= idx_max; i++) { low = (1ULL << (i + 1)) >> 1; high = (1ULL << (i + 1)) - 1; if (low == high) low -= 1; val = vals[i]; width = idx_max <= 32 ? 10 : 20; printf("%*lld -> %-*lld : %-8d |", width, low, width, high, val); print_stars(val, val_max, stars); printf("|\n"); } } void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, unsigned int step, const char *val_type) { int i, stars_max = 40, idx_min = -1, idx_max = -1; unsigned int val, val_max = 0; for (i = 0; i < vals_size; i++) { val = vals[i]; if (val > 0) { idx_max = i; if (idx_min < 0) idx_min = i; } if (val > val_max) val_max = val; } if (idx_max < 0) return; printf(" %-13s : count distribution\n", val_type); for (i = idx_min; i <= idx_max; i++) { val = vals[i]; printf(" %-10d : %-8d |", base + i * step, val); print_stars(val, val_max, stars_max); printf("|\n"); } } unsigned long long get_ktime_ns(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; } int bump_memlock_rlimit(void) { struct rlimit rlim_new = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY, }; return setrlimit(RLIMIT_MEMLOCK, &rlim_new); } bool is_kernel_module(const char *name) { bool found = false; char buf[64]; FILE *f; f = fopen("/proc/modules", "r"); if (!f) return false; while (fgets(buf, sizeof(buf), f) != NULL) { if (sscanf(buf, "%s %*s\n", buf) != 1) break; if (!strcmp(buf, name)) { found = true; break; } } fclose(f); return found; }