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;
}