Path: blob/master/tools/tracing/latency/latency-collector.c
29271 views
// SPDX-License-Identifier: GPL-2.01/*2* Copyright (C) 2017, 2018, 2019, 2021 BMW Car IT GmbH3* Author: Viktor Rosendahl ([email protected])4*/56#define _GNU_SOURCE7#define _POSIX_C_SOURCE 200809L89#include <ctype.h>10#include <stdbool.h>11#include <stdio.h>12#include <stdlib.h>13#include <string.h>1415#include <err.h>16#include <errno.h>17#include <fcntl.h>18#include <getopt.h>19#include <sched.h>20#include <linux/unistd.h>21#include <signal.h>22#include <sys/inotify.h>23#include <unistd.h>24#include <pthread.h>25#include <tracefs.h>2627static const char *prg_name;28static const char *prg_unknown = "unknown program name";2930static int fd_stdout;3132static int sched_policy;33static bool sched_policy_set;3435static int sched_pri;36static bool sched_pri_set;3738static bool trace_enable = true;39static bool setup_ftrace = true;40static bool use_random_sleep;4142#define TRACE_OPTS \43C(FUNC_TR, "function-trace"), \44C(DISP_GR, "display-graph"), \45C(NR, NULL)4647#undef C48#define C(a, b) OPTIDX_##a4950enum traceopt {51TRACE_OPTS52};5354#undef C55#define C(a, b) b5657static const char *const optstr[] = {58TRACE_OPTS59};6061enum errhandling {62ERR_EXIT = 0,63ERR_WARN,64ERR_CLEANUP,65};6667static bool use_options[OPTIDX_NR];6869static char inotify_buffer[655360];7071#define likely(x) __builtin_expect(!!(x), 1)72#define unlikely(x) __builtin_expect(!!(x), 0)73#define bool2str(x) (x ? "true":"false")7475#define DEFAULT_NR_PRINTER_THREADS (3)76static unsigned int nr_threads = DEFAULT_NR_PRINTER_THREADS;7778#define DEFAULT_TABLE_SIZE (2)79static unsigned int table_startsize = DEFAULT_TABLE_SIZE;8081static int verbosity;8283#define verbose_sizechange() (verbosity >= 1)84#define verbose_lostevent() (verbosity >= 2)85#define verbose_ftrace() (verbosity >= 1)8687#define was_changed(ORIG, CUR) (strcmp(ORIG, CUR) != 0)88#define needs_change(CUR, WANTED) (strcmp(CUR, WANTED) != 0)8990static const char *debug_tracefile;91static const char *debug_tracefile_dflt;92static const char *debug_maxlat;93static const char *debug_maxlat_dflt;94static const char * const DEBUG_NOFILE = "[file not found]";9596static const char * const TR_MAXLAT = "tracing_max_latency";97static const char * const TR_THRESH = "tracing_thresh";98static const char * const TR_CURRENT = "current_tracer";99static const char * const TR_OPTIONS = "trace_options";100101static const char * const NOP_TRACER = "nop";102103static const char * const OPT_NO_PREFIX = "no";104105#define DFLT_THRESHOLD_US "0"106static const char *threshold = DFLT_THRESHOLD_US;107108#define DEV_URANDOM "/dev/urandom"109#define RT_DEFAULT_PRI (99)110#define DEFAULT_PRI (0)111112#define USEC_PER_MSEC (1000L)113#define NSEC_PER_USEC (1000L)114#define NSEC_PER_MSEC (USEC_PER_MSEC * NSEC_PER_USEC)115116#define MSEC_PER_SEC (1000L)117#define USEC_PER_SEC (USEC_PER_MSEC * MSEC_PER_SEC)118#define NSEC_PER_SEC (NSEC_PER_MSEC * MSEC_PER_SEC)119120#define SLEEP_TIME_MS_DEFAULT (1000L)121#define TRY_PRINTMUTEX_MS (1000)122123static long sleep_time = (USEC_PER_MSEC * SLEEP_TIME_MS_DEFAULT);124125static const char * const queue_full_warning =126"Could not queue trace for printing. It is likely that events happen faster\n"127"than what they can be printed. Probably partly because of random sleeping\n";128129static const char * const no_tracer_msg =130"Could not find any tracers! Running this program as root may help!\n";131132static const char * const no_latency_tr_msg =133"No latency tracers are supported by your kernel!\n";134135struct policy {136const char *name;137int policy;138int default_pri;139};140141static const struct policy policies[] = {142{ "other", SCHED_OTHER, DEFAULT_PRI },143{ "batch", SCHED_BATCH, DEFAULT_PRI },144{ "idle", SCHED_IDLE, DEFAULT_PRI },145{ "rr", SCHED_RR, RT_DEFAULT_PRI },146{ "fifo", SCHED_FIFO, RT_DEFAULT_PRI },147{ NULL, 0, DEFAULT_PRI }148};149150/*151* The default tracer will be the first on this list that is supported by the152* currently running Linux kernel.153*/154static const char * const relevant_tracers[] = {155"preemptirqsoff",156"preemptoff",157"irqsoff",158"wakeup",159"wakeup_rt",160"wakeup_dl",161NULL162};163164/* This is the list of tracers for which random sleep makes sense */165static const char * const random_tracers[] = {166"preemptirqsoff",167"preemptoff",168"irqsoff",169NULL170};171172static const char *current_tracer;173static bool force_tracer;174175struct ftrace_state {176char *tracer;177char *thresh;178bool opt[OPTIDX_NR];179bool opt_valid[OPTIDX_NR];180pthread_mutex_t mutex;181};182183struct entry {184int ticket;185int ticket_completed_ref;186};187188struct print_state {189int ticket_counter;190int ticket_completed;191pthread_mutex_t mutex;192pthread_cond_t cond;193int cnt;194pthread_mutex_t cnt_mutex;195};196197struct short_msg {198char buf[160];199int len;200};201202static struct print_state printstate;203static struct ftrace_state save_state;204volatile sig_atomic_t signal_flag;205206#define PROB_TABLE_MAX_SIZE (1000)207208int probabilities[PROB_TABLE_MAX_SIZE];209210struct sleep_table {211int *table;212int size;213pthread_mutex_t mutex;214};215216static struct sleep_table sleeptable;217218#define QUEUE_SIZE (10)219220struct queue {221struct entry entries[QUEUE_SIZE];222int next_prod_idx;223int next_cons_idx;224pthread_mutex_t mutex;225pthread_cond_t cond;226};227228#define MAX_THREADS (40)229230struct queue printqueue;231pthread_t printthread[MAX_THREADS];232pthread_mutex_t print_mtx;233#define PRINT_BUFFER_SIZE (16 * 1024 * 1024)234235static void cleanup_exit(int status);236static int set_trace_opt(const char *opt, bool value);237238static __always_inline void *malloc_or_die(size_t size)239{240void *ptr = malloc(size);241242if (unlikely(ptr == NULL)) {243warn("malloc() failed");244cleanup_exit(EXIT_FAILURE);245}246return ptr;247}248249static __always_inline void *malloc_or_die_nocleanup(size_t size)250{251void *ptr = malloc(size);252253if (unlikely(ptr == NULL))254err(0, "malloc() failed");255return ptr;256}257258static __always_inline void write_or_die(int fd, const char *buf, size_t count)259{260ssize_t r;261262do {263r = write(fd, buf, count);264if (unlikely(r < 0)) {265if (errno == EINTR)266continue;267warn("write() failed");268cleanup_exit(EXIT_FAILURE);269}270count -= r;271buf += r;272} while (count > 0);273}274275static __always_inline void clock_gettime_or_die(clockid_t clk_id,276struct timespec *tp)277{278int r = clock_gettime(clk_id, tp);279280if (unlikely(r != 0))281err(EXIT_FAILURE, "clock_gettime() failed");282}283284static __always_inline void sigemptyset_or_die(sigset_t *s)285{286if (unlikely(sigemptyset(s) != 0)) {287warn("sigemptyset() failed");288cleanup_exit(EXIT_FAILURE);289}290}291292static __always_inline void sigaddset_or_die(sigset_t *s, int signum)293{294if (unlikely(sigaddset(s, signum) != 0)) {295warn("sigemptyset() failed");296cleanup_exit(EXIT_FAILURE);297}298}299300static __always_inline void sigaction_or_die(int signum,301const struct sigaction *act,302struct sigaction *oldact)303{304if (unlikely(sigaction(signum, act, oldact) != 0)) {305warn("sigaction() failed");306cleanup_exit(EXIT_FAILURE);307}308}309310static void open_stdout(void)311{312if (setvbuf(stdout, NULL, _IONBF, 0) != 0)313err(EXIT_FAILURE, "setvbuf() failed");314fd_stdout = fileno(stdout);315if (fd_stdout < 0)316err(EXIT_FAILURE, "fileno() failed");317}318319/*320* It's not worth it to call cleanup_exit() from mutex functions because321* cleanup_exit() uses mutexes.322*/323static __always_inline void mutex_lock(pthread_mutex_t *mtx)324{325errno = pthread_mutex_lock(mtx);326if (unlikely(errno))327err(EXIT_FAILURE, "pthread_mutex_lock() failed");328}329330331static __always_inline void mutex_unlock(pthread_mutex_t *mtx)332{333errno = pthread_mutex_unlock(mtx);334if (unlikely(errno))335err(EXIT_FAILURE, "pthread_mutex_unlock() failed");336}337338static __always_inline void cond_signal(pthread_cond_t *cond)339{340errno = pthread_cond_signal(cond);341if (unlikely(errno))342err(EXIT_FAILURE, "pthread_cond_signal() failed");343}344345static __always_inline void cond_wait(pthread_cond_t *restrict cond,346pthread_mutex_t *restrict mutex)347{348errno = pthread_cond_wait(cond, mutex);349if (unlikely(errno))350err(EXIT_FAILURE, "pthread_cond_wait() failed");351}352353static __always_inline void cond_broadcast(pthread_cond_t *cond)354{355errno = pthread_cond_broadcast(cond);356if (unlikely(errno))357err(EXIT_FAILURE, "pthread_cond_broadcast() failed");358}359360static __always_inline void361mutex_init(pthread_mutex_t *mutex,362const pthread_mutexattr_t *attr)363{364errno = pthread_mutex_init(mutex, attr);365if (errno)366err(EXIT_FAILURE, "pthread_mutex_init() failed");367}368369static __always_inline void mutexattr_init(pthread_mutexattr_t *attr)370{371errno = pthread_mutexattr_init(attr);372if (errno)373err(EXIT_FAILURE, "pthread_mutexattr_init() failed");374}375376static __always_inline void mutexattr_destroy(pthread_mutexattr_t *attr)377{378errno = pthread_mutexattr_destroy(attr);379if (errno)380err(EXIT_FAILURE, "pthread_mutexattr_destroy() failed");381}382383static __always_inline void mutexattr_settype(pthread_mutexattr_t *attr,384int type)385{386errno = pthread_mutexattr_settype(attr, type);387if (errno)388err(EXIT_FAILURE, "pthread_mutexattr_settype() failed");389}390391static __always_inline void condattr_init(pthread_condattr_t *attr)392{393errno = pthread_condattr_init(attr);394if (errno)395err(EXIT_FAILURE, "pthread_condattr_init() failed");396}397398static __always_inline void condattr_destroy(pthread_condattr_t *attr)399{400errno = pthread_condattr_destroy(attr);401if (errno)402err(EXIT_FAILURE, "pthread_condattr_destroy() failed");403}404405static __always_inline void condattr_setclock(pthread_condattr_t *attr,406clockid_t clock_id)407{408errno = pthread_condattr_setclock(attr, clock_id);409if (unlikely(errno))410err(EXIT_FAILURE, "pthread_condattr_setclock() failed");411}412413static __always_inline void cond_init(pthread_cond_t *cond,414const pthread_condattr_t *attr)415{416errno = pthread_cond_init(cond, attr);417if (errno)418err(EXIT_FAILURE, "pthread_cond_init() failed");419}420421static __always_inline int422cond_timedwait(pthread_cond_t *restrict cond,423pthread_mutex_t *restrict mutex,424const struct timespec *restrict abstime)425{426errno = pthread_cond_timedwait(cond, mutex, abstime);427if (errno && errno != ETIMEDOUT)428err(EXIT_FAILURE, "pthread_cond_timedwait() failed");429return errno;430}431432static void init_printstate(void)433{434pthread_condattr_t cattr;435436printstate.ticket_counter = 0;437printstate.ticket_completed = 0;438printstate.cnt = 0;439440mutex_init(&printstate.mutex, NULL);441442condattr_init(&cattr);443condattr_setclock(&cattr, CLOCK_MONOTONIC);444cond_init(&printstate.cond, &cattr);445condattr_destroy(&cattr);446}447448static void init_print_mtx(void)449{450pthread_mutexattr_t mattr;451452mutexattr_init(&mattr);453mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);454mutex_init(&print_mtx, &mattr);455mutexattr_destroy(&mattr);456457}458459static void signal_blocking(int how)460{461sigset_t s;462463sigemptyset_or_die(&s);464sigaddset_or_die(&s, SIGHUP);465sigaddset_or_die(&s, SIGTERM);466sigaddset_or_die(&s, SIGINT);467468errno = pthread_sigmask(how, &s, NULL);469if (unlikely(errno)) {470warn("pthread_sigmask() failed");471cleanup_exit(EXIT_FAILURE);472}473}474475static void signal_handler(int num)476{477signal_flag = num;478}479480static void setup_sig_handler(void)481{482struct sigaction sa;483484memset(&sa, 0, sizeof(sa));485sa.sa_handler = signal_handler;486487sigaction_or_die(SIGHUP, &sa, NULL);488sigaction_or_die(SIGTERM, &sa, NULL);489sigaction_or_die(SIGINT, &sa, NULL);490}491492static void process_signal(int signal)493{494char *name;495496name = strsignal(signal);497if (name == NULL)498printf("Received signal %d\n", signal);499else500printf("Received signal %d (%s)\n", signal, name);501cleanup_exit(EXIT_SUCCESS);502}503504static __always_inline void check_signals(void)505{506int signal = signal_flag;507508if (unlikely(signal))509process_signal(signal);510}511512static __always_inline void get_time_in_future(struct timespec *future,513long time_us)514{515long nsec;516517clock_gettime_or_die(CLOCK_MONOTONIC, future);518future->tv_sec += time_us / USEC_PER_SEC;519nsec = future->tv_nsec + (time_us * NSEC_PER_USEC) % NSEC_PER_SEC;520if (nsec >= NSEC_PER_SEC) {521future->tv_nsec = nsec % NSEC_PER_SEC;522future->tv_sec += 1;523}524}525526static __always_inline bool time_has_passed(const struct timespec *time)527{528struct timespec now;529530clock_gettime_or_die(CLOCK_MONOTONIC, &now);531if (now.tv_sec > time->tv_sec)532return true;533if (now.tv_sec < time->tv_sec)534return false;535return (now.tv_nsec >= time->tv_nsec);536}537538static bool mutex_trylock_limit(pthread_mutex_t *mutex, int time_ms)539{540long time_us = time_ms * USEC_PER_MSEC;541struct timespec limit;542543get_time_in_future(&limit, time_us);544do {545errno = pthread_mutex_trylock(mutex);546if (errno && errno != EBUSY)547err(EXIT_FAILURE, "pthread_mutex_trylock() failed");548} while (errno && !time_has_passed(&limit));549return errno == 0;550}551552static void restore_trace_opts(const struct ftrace_state *state,553const bool *cur)554{555int i;556int r;557558for (i = 0; i < OPTIDX_NR; i++)559if (state->opt_valid[i] && state->opt[i] != cur[i]) {560r = set_trace_opt(optstr[i], state->opt[i]);561if (r < 0)562warnx("Failed to restore the %s option to %s",563optstr[i], bool2str(state->opt[i]));564else if (verbose_ftrace())565printf("Restored the %s option in %s to %s\n",566optstr[i], TR_OPTIONS,567bool2str(state->opt[i]));568}569}570571static char *read_file(const char *file, enum errhandling h)572{573int psize;574char *r;575static const char *emsg = "Failed to read the %s file";576577r = tracefs_instance_file_read(NULL, file, &psize);578if (!r) {579if (h) {580warn(emsg, file);581if (h == ERR_CLEANUP)582cleanup_exit(EXIT_FAILURE);583} else584errx(EXIT_FAILURE, emsg, file);585}586587if (r && r[psize - 1] == '\n')588r[psize - 1] = '\0';589return r;590}591592static void restore_file(const char *file, char **saved, const char *cur)593{594if (*saved && was_changed(*saved, cur)) {595if (tracefs_instance_file_write(NULL, file, *saved) < 0)596warnx("Failed to restore %s to %s!", file, *saved);597else if (verbose_ftrace())598printf("Restored %s to %s\n", file, *saved);599free(*saved);600*saved = NULL;601}602}603604static void restore_ftrace(void)605{606mutex_lock(&save_state.mutex);607608restore_file(TR_CURRENT, &save_state.tracer, current_tracer);609restore_file(TR_THRESH, &save_state.thresh, threshold);610restore_trace_opts(&save_state, use_options);611612mutex_unlock(&save_state.mutex);613}614615static void cleanup_exit(int status)616{617char *maxlat;618619if (!setup_ftrace)620exit(status);621622/*623* We try the print_mtx for 1 sec in order to avoid garbled624* output if possible, but if it cannot be obtained we proceed anyway.625*/626mutex_trylock_limit(&print_mtx, TRY_PRINTMUTEX_MS);627628maxlat = read_file(TR_MAXLAT, ERR_WARN);629if (maxlat) {630printf("The maximum detected latency was: %sus\n", maxlat);631free(maxlat);632}633634restore_ftrace();635/*636* We do not need to unlock the print_mtx here because we will exit at637* the end of this function. Unlocking print_mtx causes problems if a638* print thread happens to be waiting for the mutex because we have639* just changed the ftrace settings to the original and thus the640* print thread would output incorrect data from ftrace.641*/642exit(status);643}644645static void init_save_state(void)646{647pthread_mutexattr_t mattr;648649mutexattr_init(&mattr);650mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);651mutex_init(&save_state.mutex, &mattr);652mutexattr_destroy(&mattr);653654save_state.tracer = NULL;655save_state.thresh = NULL;656save_state.opt_valid[OPTIDX_FUNC_TR] = false;657save_state.opt_valid[OPTIDX_DISP_GR] = false;658}659660static int printstate_next_ticket(struct entry *req)661{662int r;663664r = ++(printstate.ticket_counter);665req->ticket = r;666req->ticket_completed_ref = printstate.ticket_completed;667cond_broadcast(&printstate.cond);668return r;669}670671static __always_inline672void printstate_mark_req_completed(const struct entry *req)673{674if (req->ticket > printstate.ticket_completed)675printstate.ticket_completed = req->ticket;676}677678static __always_inline679bool printstate_has_new_req_arrived(const struct entry *req)680{681return (printstate.ticket_counter != req->ticket);682}683684static __always_inline int printstate_cnt_inc(void)685{686int value;687688mutex_lock(&printstate.cnt_mutex);689value = ++printstate.cnt;690mutex_unlock(&printstate.cnt_mutex);691return value;692}693694static __always_inline int printstate_cnt_dec(void)695{696int value;697698mutex_lock(&printstate.cnt_mutex);699value = --printstate.cnt;700mutex_unlock(&printstate.cnt_mutex);701return value;702}703704static __always_inline int printstate_cnt_read(void)705{706int value;707708mutex_lock(&printstate.cnt_mutex);709value = printstate.cnt;710mutex_unlock(&printstate.cnt_mutex);711return value;712}713714static __always_inline715bool prev_req_won_race(const struct entry *req)716{717return (printstate.ticket_completed != req->ticket_completed_ref);718}719720static void sleeptable_resize(int size, bool printout, struct short_msg *msg)721{722int bytes;723724if (printout) {725msg->len = 0;726if (unlikely(size > PROB_TABLE_MAX_SIZE))727bytes = snprintf(msg->buf, sizeof(msg->buf),728"Cannot increase probability table to %d (maximum size reached)\n", size);729else730bytes = snprintf(msg->buf, sizeof(msg->buf),731"Increasing probability table to %d\n", size);732if (bytes < 0)733warn("snprintf() failed");734else735msg->len = bytes;736}737738if (unlikely(size < 0)) {739/* Should never happen */740warnx("Bad program state at %s:%d", __FILE__, __LINE__);741cleanup_exit(EXIT_FAILURE);742return;743}744sleeptable.size = size;745sleeptable.table = &probabilities[PROB_TABLE_MAX_SIZE - size];746}747748static void init_probabilities(void)749{750int i;751int j = 1000;752753for (i = 0; i < PROB_TABLE_MAX_SIZE; i++) {754probabilities[i] = 1000 / j;755j--;756}757mutex_init(&sleeptable.mutex, NULL);758}759760static int table_get_probability(const struct entry *req,761struct short_msg *msg)762{763int diff = req->ticket - req->ticket_completed_ref;764int rval = 0;765766msg->len = 0;767diff--;768/* Should never happen...*/769if (unlikely(diff < 0)) {770warnx("Programmer assumption error at %s:%d\n", __FILE__,771__LINE__);772cleanup_exit(EXIT_FAILURE);773}774mutex_lock(&sleeptable.mutex);775if (diff >= (sleeptable.size - 1)) {776rval = sleeptable.table[sleeptable.size - 1];777sleeptable_resize(sleeptable.size + 1, verbose_sizechange(),778msg);779} else {780rval = sleeptable.table[diff];781}782mutex_unlock(&sleeptable.mutex);783return rval;784}785786static void init_queue(struct queue *q)787{788q->next_prod_idx = 0;789q->next_cons_idx = 0;790mutex_init(&q->mutex, NULL);791errno = pthread_cond_init(&q->cond, NULL);792if (errno)793err(EXIT_FAILURE, "pthread_cond_init() failed");794}795796static __always_inline int queue_len(const struct queue *q)797{798if (q->next_prod_idx >= q->next_cons_idx)799return q->next_prod_idx - q->next_cons_idx;800else801return QUEUE_SIZE - q->next_cons_idx + q->next_prod_idx;802}803804static __always_inline int queue_nr_free(const struct queue *q)805{806int nr_free = QUEUE_SIZE - queue_len(q);807808/*809* If there is only one slot left we will anyway lie and claim that the810* queue is full because adding an element will make it appear empty811*/812if (nr_free == 1)813nr_free = 0;814return nr_free;815}816817static __always_inline void queue_idx_inc(int *idx)818{819*idx = (*idx + 1) % QUEUE_SIZE;820}821822static __always_inline void queue_push_to_back(struct queue *q,823const struct entry *e)824{825q->entries[q->next_prod_idx] = *e;826queue_idx_inc(&q->next_prod_idx);827}828829static __always_inline struct entry queue_pop_from_front(struct queue *q)830{831struct entry e = q->entries[q->next_cons_idx];832833queue_idx_inc(&q->next_cons_idx);834return e;835}836837static __always_inline void queue_cond_signal(struct queue *q)838{839cond_signal(&q->cond);840}841842static __always_inline void queue_cond_wait(struct queue *q)843{844cond_wait(&q->cond, &q->mutex);845}846847static __always_inline int queue_try_to_add_entry(struct queue *q,848const struct entry *e)849{850int r = 0;851852mutex_lock(&q->mutex);853if (queue_nr_free(q) > 0) {854queue_push_to_back(q, e);855cond_signal(&q->cond);856} else857r = -1;858mutex_unlock(&q->mutex);859return r;860}861862static struct entry queue_wait_for_entry(struct queue *q)863{864struct entry e;865866mutex_lock(&q->mutex);867while (true) {868if (queue_len(&printqueue) > 0) {869e = queue_pop_from_front(q);870break;871}872queue_cond_wait(q);873}874mutex_unlock(&q->mutex);875876return e;877}878879static const struct policy *policy_from_name(const char *name)880{881const struct policy *p = &policies[0];882883while (p->name != NULL) {884if (!strcmp(name, p->name))885return p;886p++;887}888return NULL;889}890891static const char *policy_name(int policy)892{893const struct policy *p = &policies[0];894static const char *rval = "unknown";895896while (p->name != NULL) {897if (p->policy == policy)898return p->name;899p++;900}901return rval;902}903904static bool is_relevant_tracer(const char *name)905{906unsigned int i;907908for (i = 0; relevant_tracers[i]; i++)909if (!strcmp(name, relevant_tracers[i]))910return true;911return false;912}913914static bool random_makes_sense(const char *name)915{916unsigned int i;917918for (i = 0; random_tracers[i]; i++)919if (!strcmp(name, random_tracers[i]))920return true;921return false;922}923924static void show_available(void)925{926char **tracers;927int found = 0;928int i;929930tracers = tracefs_tracers(NULL);931for (i = 0; tracers && tracers[i]; i++) {932if (is_relevant_tracer(tracers[i]))933found++;934}935936if (!tracers) {937warnx("%s", no_tracer_msg);938return;939}940941if (!found) {942warnx("%s", no_latency_tr_msg);943tracefs_list_free(tracers);944return;945}946947printf("The following latency tracers are available on your system:\n");948for (i = 0; tracers[i]; i++) {949if (is_relevant_tracer(tracers[i]))950printf("%s\n", tracers[i]);951}952tracefs_list_free(tracers);953}954955static bool tracer_valid(const char *name, bool *notracer)956{957char **tracers;958int i;959bool rval = false;960961*notracer = false;962tracers = tracefs_tracers(NULL);963if (!tracers) {964*notracer = true;965return false;966}967for (i = 0; tracers[i]; i++)968if (!strcmp(tracers[i], name)) {969rval = true;970break;971}972tracefs_list_free(tracers);973return rval;974}975976static const char *find_default_tracer(void)977{978int i;979bool notracer;980bool valid;981982for (i = 0; relevant_tracers[i]; i++) {983valid = tracer_valid(relevant_tracers[i], ¬racer);984if (notracer)985errx(EXIT_FAILURE, "%s", no_tracer_msg);986if (valid)987return relevant_tracers[i];988}989return NULL;990}991992static bool toss_coin(struct drand48_data *buffer, unsigned int prob)993{994long r;995996if (unlikely(lrand48_r(buffer, &r))) {997warnx("lrand48_r() failed");998cleanup_exit(EXIT_FAILURE);999}1000r = r % 1000L;1001if (r < prob)1002return true;1003else1004return false;1005}100610071008static long go_to_sleep(const struct entry *req)1009{1010struct timespec future;1011long delay = sleep_time;10121013get_time_in_future(&future, delay);10141015mutex_lock(&printstate.mutex);1016while (!printstate_has_new_req_arrived(req)) {1017cond_timedwait(&printstate.cond, &printstate.mutex, &future);1018if (time_has_passed(&future))1019break;1020}10211022if (printstate_has_new_req_arrived(req))1023delay = -1;1024mutex_unlock(&printstate.mutex);10251026return delay;1027}102810291030static void set_priority(void)1031{1032int r;1033pid_t pid;1034struct sched_param param;10351036memset(¶m, 0, sizeof(param));1037param.sched_priority = sched_pri;10381039pid = getpid();1040r = sched_setscheduler(pid, sched_policy, ¶m);10411042if (r != 0)1043err(EXIT_FAILURE, "sched_setscheduler() failed");1044}10451046pid_t latency_collector_gettid(void)1047{1048return (pid_t) syscall(__NR_gettid);1049}10501051static void print_priority(void)1052{1053pid_t tid;1054int policy;1055int r;1056struct sched_param param;10571058tid = latency_collector_gettid();1059r = pthread_getschedparam(pthread_self(), &policy, ¶m);1060if (r != 0) {1061warn("pthread_getschedparam() failed");1062cleanup_exit(EXIT_FAILURE);1063}1064mutex_lock(&print_mtx);1065printf("Thread %d runs with scheduling policy %s and priority %d\n",1066tid, policy_name(policy), param.sched_priority);1067mutex_unlock(&print_mtx);1068}10691070static __always_inline1071void __print_skipmessage(const struct short_msg *resize_msg,1072const struct timespec *timestamp, char *buffer,1073size_t bufspace, const struct entry *req, bool excuse,1074const char *str)1075{1076ssize_t bytes = 0;1077char *p = &buffer[0];1078long us, sec;1079int r;10801081sec = timestamp->tv_sec;1082us = timestamp->tv_nsec / 1000;10831084if (resize_msg != NULL && resize_msg->len > 0) {1085strncpy(p, resize_msg->buf, resize_msg->len);1086bytes += resize_msg->len;1087p += resize_msg->len;1088bufspace -= resize_msg->len;1089}10901091if (excuse)1092r = snprintf(p, bufspace,1093"%ld.%06ld Latency %d printout skipped due to %s\n",1094sec, us, req->ticket, str);1095else1096r = snprintf(p, bufspace, "%ld.%06ld Latency %d detected\n",1097sec, us, req->ticket);10981099if (r < 0)1100warn("snprintf() failed");1101else1102bytes += r;11031104/* These prints could happen concurrently */1105mutex_lock(&print_mtx);1106write_or_die(fd_stdout, buffer, bytes);1107mutex_unlock(&print_mtx);1108}11091110static void print_skipmessage(const struct short_msg *resize_msg,1111const struct timespec *timestamp, char *buffer,1112size_t bufspace, const struct entry *req,1113bool excuse)1114{1115__print_skipmessage(resize_msg, timestamp, buffer, bufspace, req,1116excuse, "random delay");1117}11181119static void print_lostmessage(const struct timespec *timestamp, char *buffer,1120size_t bufspace, const struct entry *req,1121const char *reason)1122{1123__print_skipmessage(NULL, timestamp, buffer, bufspace, req, true,1124reason);1125}11261127static void print_tracefile(const struct short_msg *resize_msg,1128const struct timespec *timestamp, char *buffer,1129size_t bufspace, long slept,1130const struct entry *req)1131{1132static const int reserve = 256;1133char *p = &buffer[0];1134ssize_t bytes = 0;1135ssize_t bytes_tot = 0;1136long us, sec;1137long slept_ms;1138int trace_fd;11391140/* Save some space for the final string and final null char */1141bufspace = bufspace - reserve - 1;11421143if (resize_msg != NULL && resize_msg->len > 0) {1144bytes = resize_msg->len;1145strncpy(p, resize_msg->buf, bytes);1146bytes_tot += bytes;1147p += bytes;1148bufspace -= bytes;1149}11501151trace_fd = open(debug_tracefile, O_RDONLY);11521153if (trace_fd < 0) {1154warn("open() failed on %s", debug_tracefile);1155return;1156}11571158sec = timestamp->tv_sec;1159us = timestamp->tv_nsec / 1000;11601161if (slept != 0) {1162slept_ms = slept / 1000;1163bytes = snprintf(p, bufspace,1164"%ld.%06ld Latency %d randomly sleep for %ld ms before print\n",1165sec, us, req->ticket, slept_ms);1166} else {1167bytes = snprintf(p, bufspace,1168"%ld.%06ld Latency %d immediate print\n", sec,1169us, req->ticket);1170}11711172if (bytes < 0) {1173warn("snprintf() failed");1174return;1175}1176p += bytes;1177bufspace -= bytes;1178bytes_tot += bytes;11791180bytes = snprintf(p, bufspace,1181">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> BEGIN <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"1182);11831184if (bytes < 0) {1185warn("snprintf() failed");1186return;1187}11881189p += bytes;1190bufspace -= bytes;1191bytes_tot += bytes;11921193do {1194bytes = read(trace_fd, p, bufspace);1195if (bytes < 0) {1196if (errno == EINTR)1197continue;1198warn("read() failed on %s", debug_tracefile);1199if (unlikely(close(trace_fd) != 0))1200warn("close() failed on %s", debug_tracefile);1201return;1202}1203if (bytes == 0)1204break;1205p += bytes;1206bufspace -= bytes;1207bytes_tot += bytes;1208} while (true);12091210if (unlikely(close(trace_fd) != 0))1211warn("close() failed on %s", debug_tracefile);12121213printstate_cnt_dec();1214/* Add the reserve space back to the budget for the final string */1215bufspace += reserve;12161217bytes = snprintf(p, bufspace,1218">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n");12191220if (bytes < 0) {1221warn("snprintf() failed");1222return;1223}12241225bytes_tot += bytes;12261227/* These prints could happen concurrently */1228mutex_lock(&print_mtx);1229write_or_die(fd_stdout, buffer, bytes_tot);1230mutex_unlock(&print_mtx);1231}12321233static char *get_no_opt(const char *opt)1234{1235char *no_opt;1236int s;12371238s = strlen(opt) + strlen(OPT_NO_PREFIX) + 1;1239/* We may be called from cleanup_exit() via set_trace_opt() */1240no_opt = malloc_or_die_nocleanup(s);1241strcpy(no_opt, OPT_NO_PREFIX);1242strcat(no_opt, opt);1243return no_opt;1244}12451246static char *find_next_optstr(const char *allopt, const char **next)1247{1248const char *begin;1249const char *end;1250char *r;1251int s = 0;12521253if (allopt == NULL)1254return NULL;12551256for (begin = allopt; *begin != '\0'; begin++) {1257if (isgraph(*begin))1258break;1259}12601261if (*begin == '\0')1262return NULL;12631264for (end = begin; *end != '\0' && isgraph(*end); end++)1265s++;12661267r = malloc_or_die_nocleanup(s + 1);1268strncpy(r, begin, s);1269r[s] = '\0';1270*next = begin + s;1271return r;1272}12731274static bool get_trace_opt(const char *allopt, const char *opt, bool *found)1275{1276*found = false;1277char *no_opt;1278char *str;1279const char *next = allopt;1280bool rval = false;12811282no_opt = get_no_opt(opt);12831284do {1285str = find_next_optstr(next, &next);1286if (str == NULL)1287break;1288if (!strcmp(str, opt)) {1289*found = true;1290rval = true;1291free(str);1292break;1293}1294if (!strcmp(str, no_opt)) {1295*found = true;1296rval = false;1297free(str);1298break;1299}1300free(str);1301} while (true);1302free(no_opt);13031304return rval;1305}13061307static int set_trace_opt(const char *opt, bool value)1308{1309char *str;1310int r;13111312if (value)1313str = strdup(opt);1314else1315str = get_no_opt(opt);13161317r = tracefs_instance_file_write(NULL, TR_OPTIONS, str);1318free(str);1319return r;1320}13211322void save_trace_opts(struct ftrace_state *state)1323{1324char *allopt;1325int psize;1326int i;13271328allopt = tracefs_instance_file_read(NULL, TR_OPTIONS, &psize);1329if (!allopt)1330errx(EXIT_FAILURE, "Failed to read the %s file\n", TR_OPTIONS);13311332for (i = 0; i < OPTIDX_NR; i++)1333state->opt[i] = get_trace_opt(allopt, optstr[i],1334&state->opt_valid[i]);13351336free(allopt);1337}13381339static void write_file(const char *file, const char *cur, const char *new,1340enum errhandling h)1341{1342int r;1343static const char *emsg = "Failed to write to the %s file!";13441345/* Do nothing if we now that the current and new value are equal */1346if (cur && !needs_change(cur, new))1347return;13481349r = tracefs_instance_file_write(NULL, file, new);1350if (r < 0) {1351if (h) {1352warnx(emsg, file);1353if (h == ERR_CLEANUP)1354cleanup_exit(EXIT_FAILURE);1355} else1356errx(EXIT_FAILURE, emsg, file);1357}1358if (verbose_ftrace()) {1359mutex_lock(&print_mtx);1360printf("%s was set to %s\n", file, new);1361mutex_unlock(&print_mtx);1362}1363}13641365static void reset_max_latency(void)1366{1367write_file(TR_MAXLAT, NULL, "0", ERR_CLEANUP);1368}13691370static void save_and_disable_tracer(void)1371{1372char *orig_th;1373char *tracer;1374bool need_nop = false;13751376mutex_lock(&save_state.mutex);13771378save_trace_opts(&save_state);1379tracer = read_file(TR_CURRENT, ERR_EXIT);1380orig_th = read_file(TR_THRESH, ERR_EXIT);13811382if (needs_change(tracer, NOP_TRACER)) {1383mutex_lock(&print_mtx);1384if (force_tracer) {1385printf(1386"The %s tracer is already in use but proceeding anyway!\n",1387tracer);1388} else {1389printf(1390"The %s tracer is already in use, cowardly bailing out!\n"1391"This could indicate that another program or instance is tracing.\n"1392"Use the -F [--force] option to disregard the current tracer.\n", tracer);1393exit(0);1394}1395mutex_unlock(&print_mtx);1396need_nop = true;1397}13981399save_state.tracer = tracer;1400save_state.thresh = orig_th;14011402if (need_nop)1403write_file(TR_CURRENT, NULL, NOP_TRACER, ERR_EXIT);14041405mutex_unlock(&save_state.mutex);1406}14071408void set_trace_opts(struct ftrace_state *state, bool *new)1409{1410int i;1411int r;14121413/*1414* We only set options if we earlier detected that the option exists in1415* the trace_options file and that the wanted setting is different from1416* the one we saw in save_and_disable_tracer()1417*/1418for (i = 0; i < OPTIDX_NR; i++)1419if (state->opt_valid[i] &&1420state->opt[i] != new[i]) {1421r = set_trace_opt(optstr[i], new[i]);1422if (r < 0) {1423warnx("Failed to set the %s option to %s",1424optstr[i], bool2str(new[i]));1425cleanup_exit(EXIT_FAILURE);1426}1427if (verbose_ftrace()) {1428mutex_lock(&print_mtx);1429printf("%s in %s was set to %s\n", optstr[i],1430TR_OPTIONS, bool2str(new[i]));1431mutex_unlock(&print_mtx);1432}1433}1434}14351436static void enable_tracer(void)1437{1438mutex_lock(&save_state.mutex);1439set_trace_opts(&save_state, use_options);14401441write_file(TR_THRESH, save_state.thresh, threshold, ERR_CLEANUP);1442write_file(TR_CURRENT, NOP_TRACER, current_tracer, ERR_CLEANUP);14431444mutex_unlock(&save_state.mutex);1445}14461447static void tracing_loop(void)1448{1449int ifd = inotify_init();1450int wd;1451const ssize_t bufsize = sizeof(inotify_buffer);1452const ssize_t istructsize = sizeof(struct inotify_event);1453char *buf = &inotify_buffer[0];1454ssize_t nr_read;1455char *p;1456int modified;1457struct inotify_event *event;1458struct entry req;1459char *buffer;1460const size_t bufspace = PRINT_BUFFER_SIZE;1461struct timespec timestamp;14621463print_priority();14641465buffer = malloc_or_die(bufspace);14661467if (ifd < 0)1468err(EXIT_FAILURE, "inotify_init() failed!");146914701471if (setup_ftrace) {1472/*1473* We must disable the tracer before resetting the max_latency1474*/1475save_and_disable_tracer();1476/*1477* We must reset the max_latency before the inotify_add_watch()1478* call.1479*/1480reset_max_latency();1481}14821483wd = inotify_add_watch(ifd, debug_maxlat, IN_MODIFY);1484if (wd < 0)1485err(EXIT_FAILURE, "inotify_add_watch() failed!");14861487if (setup_ftrace)1488enable_tracer();14891490signal_blocking(SIG_UNBLOCK);14911492while (true) {1493modified = 0;1494check_signals();1495nr_read = read(ifd, buf, bufsize);1496check_signals();1497if (nr_read < 0) {1498if (errno == EINTR)1499continue;1500warn("read() failed on inotify fd!");1501cleanup_exit(EXIT_FAILURE);1502}1503if (nr_read == bufsize)1504warnx("inotify() buffer filled, skipping events");1505if (nr_read < istructsize) {1506warnx("read() returned too few bytes on inotify fd");1507cleanup_exit(EXIT_FAILURE);1508}15091510for (p = buf; p < buf + nr_read;) {1511event = (struct inotify_event *) p;1512if ((event->mask & IN_MODIFY) != 0)1513modified++;1514p += istructsize + event->len;1515}1516while (modified > 0) {1517check_signals();1518mutex_lock(&printstate.mutex);1519check_signals();1520printstate_next_ticket(&req);1521if (printstate_cnt_read() > 0) {1522printstate_mark_req_completed(&req);1523mutex_unlock(&printstate.mutex);1524if (verbose_lostevent()) {1525clock_gettime_or_die(CLOCK_MONOTONIC,1526×tamp);1527print_lostmessage(×tamp, buffer,1528bufspace, &req,1529"inotify loop");1530}1531break;1532}1533mutex_unlock(&printstate.mutex);1534if (queue_try_to_add_entry(&printqueue, &req) != 0) {1535/* These prints could happen concurrently */1536check_signals();1537mutex_lock(&print_mtx);1538check_signals();1539write_or_die(fd_stdout, queue_full_warning,1540strlen(queue_full_warning));1541mutex_unlock(&print_mtx);1542}1543modified--;1544}1545}1546}15471548static void *do_printloop(void *arg)1549{1550const size_t bufspace = PRINT_BUFFER_SIZE;1551char *buffer;1552long *rseed = (long *) arg;1553struct drand48_data drandbuf;1554long slept = 0;1555struct entry req;1556int prob = 0;1557struct timespec timestamp;1558struct short_msg resize_msg;15591560print_priority();15611562if (srand48_r(*rseed, &drandbuf) != 0) {1563warn("srand48_r() failed!\n");1564cleanup_exit(EXIT_FAILURE);1565}15661567buffer = malloc_or_die(bufspace);15681569while (true) {1570req = queue_wait_for_entry(&printqueue);1571clock_gettime_or_die(CLOCK_MONOTONIC, ×tamp);1572mutex_lock(&printstate.mutex);1573if (prev_req_won_race(&req)) {1574printstate_mark_req_completed(&req);1575mutex_unlock(&printstate.mutex);1576if (verbose_lostevent())1577print_lostmessage(×tamp, buffer, bufspace,1578&req, "print loop");1579continue;1580}1581mutex_unlock(&printstate.mutex);15821583/*1584* Toss a coin to decide if we want to sleep before printing1585* out the backtrace. The reason for this is that opening1586* /sys/kernel/tracing/trace will cause a blackout of1587* hundreds of ms, where no latencies will be noted by the1588* latency tracer. Thus by randomly sleeping we try to avoid1589* missing traces systematically due to this. With this option1590* we will sometimes get the first latency, some other times1591* some of the later ones, in case of closely spaced traces.1592*/1593if (trace_enable && use_random_sleep) {1594slept = 0;1595prob = table_get_probability(&req, &resize_msg);1596if (!toss_coin(&drandbuf, prob))1597slept = go_to_sleep(&req);1598if (slept >= 0) {1599/* A print is ongoing */1600printstate_cnt_inc();1601/*1602* We will do the printout below so we have to1603* mark it as completed while we still have the1604* mutex.1605*/1606mutex_lock(&printstate.mutex);1607printstate_mark_req_completed(&req);1608mutex_unlock(&printstate.mutex);1609}1610}1611if (trace_enable) {1612/*1613* slept < 0 means that we detected another1614* notification in go_to_sleep() above1615*/1616if (slept >= 0)1617/*1618* N.B. printstate_cnt_dec(); will be called1619* inside print_tracefile()1620*/1621print_tracefile(&resize_msg, ×tamp, buffer,1622bufspace, slept, &req);1623else1624print_skipmessage(&resize_msg, ×tamp,1625buffer, bufspace, &req, true);1626} else {1627print_skipmessage(&resize_msg, ×tamp, buffer,1628bufspace, &req, false);1629}1630}1631return NULL;1632}16331634static void start_printthread(void)1635{1636unsigned int i;1637long *seed;1638int ufd;16391640ufd = open(DEV_URANDOM, O_RDONLY);1641if (nr_threads > MAX_THREADS) {1642warnx(1643"Number of requested print threads was %d, max number is %d\n",1644nr_threads, MAX_THREADS);1645nr_threads = MAX_THREADS;1646}1647for (i = 0; i < nr_threads; i++) {1648seed = malloc_or_die(sizeof(*seed));1649if (ufd < 0 ||1650read(ufd, seed, sizeof(*seed)) != sizeof(*seed)) {1651printf(1652"Warning! Using trivial random number seed, since %s not available\n",1653DEV_URANDOM);1654fflush(stdout);1655*seed = i;1656}1657errno = pthread_create(&printthread[i], NULL, do_printloop,1658seed);1659if (errno)1660err(EXIT_FAILURE, "pthread_create()");1661}1662if (ufd > 0 && close(ufd) != 0)1663warn("close() failed");1664}16651666static void show_usage(void)1667{1668printf(1669"Usage: %s [OPTION]...\n\n"1670"Collect closely occurring latencies from %s\n"1671"with any of the following tracers: preemptirqsoff, preemptoff, irqsoff, "1672"wakeup,\nwakeup_dl, or wakeup_rt.\n\n"16731674"The occurrence of a latency is detected by monitoring the file\n"1675"%s with inotify.\n\n"16761677"The following options are supported:\n\n"16781679"-l, --list\t\tList the latency tracers that are supported by the\n"1680"\t\t\tcurrently running Linux kernel. If you don't see the\n"1681"\t\t\ttracer that you want, you will probably need to\n"1682"\t\t\tchange your kernel config and build a new kernel.\n\n"16831684"-t, --tracer TR\t\tUse the tracer TR. The default is to use the first\n"1685"\t\t\ttracer that is supported by the kernel in the following\n"1686"\t\t\torder of precedence:\n\n"1687"\t\t\tpreemptirqsoff\n"1688"\t\t\tpreemptoff\n"1689"\t\t\tirqsoff\n"1690"\t\t\twakeup\n"1691"\t\t\twakeup_rt\n"1692"\t\t\twakeup_dl\n"1693"\n"1694"\t\t\tIf TR is not on the list above, then a warning will be\n"1695"\t\t\tprinted.\n\n"16961697"-F, --force\t\tProceed even if another ftrace tracer is active. Without\n"1698"\t\t\tthis option, the program will refuse to start tracing if\n"1699"\t\t\tany other tracer than the nop tracer is active.\n\n"17001701"-s, --threshold TH\tConfigure ftrace to use a threshold of TH microseconds\n"1702"\t\t\tfor the tracer. The default is 0, which means that\n"1703"\t\t\ttracing_max_latency will be used. tracing_max_latency is\n"1704"\t\t\tset to 0 when the program is started and contains the\n"1705"\t\t\tmaximum of the latencies that have been encountered.\n\n"17061707"-f, --function\t\tEnable the function-trace option in trace_options. With\n"1708"\t\t\tthis option, ftrace will trace the functions that are\n"1709"\t\t\texecuted during a latency, without it we only get the\n"1710"\t\t\tbeginning, end, and backtrace.\n\n"17111712"-g, --graph\t\tEnable the display-graph option in trace_option. This\n"1713"\t\t\toption causes ftrace to show the graph of how functions\n"1714"\t\t\tare calling other functions.\n\n"17151716"-c, --policy POL\tRun the program with scheduling policy POL. POL can be\n"1717"\t\t\tother, batch, idle, rr or fifo. The default is rr. When\n"1718"\t\t\tusing rr or fifo, remember that these policies may cause\n"1719"\t\t\tother tasks to experience latencies.\n\n"17201721"-p, --priority PRI\tRun the program with priority PRI. The acceptable range\n"1722"\t\t\tof PRI depends on the scheduling policy.\n\n"17231724"-n, --notrace\t\tIf latency is detected, do not print out the content of\n"1725"\t\t\tthe trace file to standard output\n\n"17261727"-t, --threads NRTHR\tRun NRTHR threads for printing. Default is %d.\n\n"17281729"-r, --random\t\tArbitrarily sleep a certain amount of time, default\n"1730"\t\t\t%ld ms, before reading the trace file. The\n"1731"\t\t\tprobabilities for sleep are chosen so that the\n"1732"\t\t\tprobability of obtaining any of a cluster of closely\n"1733"\t\t\toccurring latencies are equal, i.e. we will randomly\n"1734"\t\t\tchoose which one we collect from the trace file.\n\n"1735"\t\t\tThis option is probably only useful with the irqsoff,\n"1736"\t\t\tpreemptoff, and preemptirqsoff tracers.\n\n"17371738"-a, --nrlat NRLAT\tFor the purpose of arbitrary delay, assume that there\n"1739"\t\t\tare no more than NRLAT clustered latencies. If NRLAT\n"1740"\t\t\tlatencies are detected during a run, this value will\n"1741"\t\t\tautomatically be increased to NRLAT + 1 and then to\n"1742"\t\t\tNRLAT + 2 and so on. The default is %d. This option\n"1743"\t\t\timplies -r. We need to know this number in order to\n"1744"\t\t\tbe able to calculate the probabilities of sleeping.\n"1745"\t\t\tSpecifically, the probabilities of not sleeping, i.e. to\n"1746"\t\t\tdo an immediate printout will be:\n\n"1747"\t\t\t1/NRLAT 1/(NRLAT - 1) ... 1/3 1/2 1\n\n"1748"\t\t\tThe probability of sleeping will be:\n\n"1749"\t\t\t1 - P, where P is from the series above\n\n"1750"\t\t\tThis descending probability will cause us to choose\n"1751"\t\t\tan occurrence at random. Observe that the final\n"1752"\t\t\tprobability is 0, it is when we reach this probability\n"1753"\t\t\tthat we increase NRLAT automatically. As an example,\n"1754"\t\t\twith the default value of 2, the probabilities will be:\n\n"1755"\t\t\t1/2 0\n\n"1756"\t\t\tThis means, when a latency is detected we will sleep\n"1757"\t\t\twith 50%% probability. If we ever detect another latency\n"1758"\t\t\tduring the sleep period, then the probability of sleep\n"1759"\t\t\twill be 0%% and the table will be expanded to:\n\n"1760"\t\t\t1/3 1/2 0\n\n"17611762"-v, --verbose\t\tIncrease the verbosity. If this option is given once,\n"1763"\t\t\tthen print a message every time that the NRLAT value\n"1764"\t\t\tis automatically increased. It also causes a message to\n"1765"\t\t\tbe printed when the ftrace settings are changed. If this\n"1766"\t\t\toption is given at least twice, then also print a\n"1767"\t\t\twarning for lost events.\n\n"17681769"-u, --time TIME\t\tArbitrarily sleep for a specified time TIME ms before\n"1770"\t\t\tprinting out the trace from the trace file. The default\n"1771"\t\t\tis %ld ms. This option implies -r.\n\n"17721773"-x, --no-ftrace\t\tDo not configure ftrace. This assume that the user\n"1774"\t\t\tconfigures the ftrace files in sysfs such as\n"1775"\t\t\t/sys/kernel/tracing/current_tracer or equivalent.\n\n"17761777"-i, --tracefile FILE\tUse FILE as trace file. The default is\n"1778"\t\t\t%s.\n"1779"\t\t\tThis options implies -x\n\n"17801781"-m, --max-lat FILE\tUse FILE as tracing_max_latency file. The default is\n"1782"\t\t\t%s.\n"1783"\t\t\tThis options implies -x\n\n"1784,1785prg_name, debug_tracefile_dflt, debug_maxlat_dflt, DEFAULT_NR_PRINTER_THREADS,1786SLEEP_TIME_MS_DEFAULT, DEFAULT_TABLE_SIZE, SLEEP_TIME_MS_DEFAULT,1787debug_tracefile_dflt, debug_maxlat_dflt);1788}17891790static void find_tracefiles(void)1791{1792debug_tracefile_dflt = tracefs_get_tracing_file("trace");1793if (debug_tracefile_dflt == NULL) {1794/* This is needed in show_usage() */1795debug_tracefile_dflt = DEBUG_NOFILE;1796}17971798debug_maxlat_dflt = tracefs_get_tracing_file("tracing_max_latency");1799if (debug_maxlat_dflt == NULL) {1800/* This is needed in show_usage() */1801debug_maxlat_dflt = DEBUG_NOFILE;1802}18031804debug_tracefile = debug_tracefile_dflt;1805debug_maxlat = debug_maxlat_dflt;1806}18071808bool alldigits(const char *s)1809{1810for (; *s != '\0'; s++)1811if (!isdigit(*s))1812return false;1813return true;1814}18151816void check_alldigits(const char *optarg, const char *argname)1817{1818if (!alldigits(optarg))1819errx(EXIT_FAILURE,1820"The %s parameter expects a decimal argument\n", argname);1821}18221823static void scan_arguments(int argc, char *argv[])1824{1825int c;1826int i;1827int option_idx = 0;18281829static struct option long_options[] = {1830{ "list", no_argument, 0, 'l' },1831{ "tracer", required_argument, 0, 't' },1832{ "force", no_argument, 0, 'F' },1833{ "threshold", required_argument, 0, 's' },1834{ "function", no_argument, 0, 'f' },1835{ "graph", no_argument, 0, 'g' },1836{ "policy", required_argument, 0, 'c' },1837{ "priority", required_argument, 0, 'p' },1838{ "help", no_argument, 0, 'h' },1839{ "notrace", no_argument, 0, 'n' },1840{ "random", no_argument, 0, 'r' },1841{ "nrlat", required_argument, 0, 'a' },1842{ "threads", required_argument, 0, 'e' },1843{ "time", required_argument, 0, 'u' },1844{ "verbose", no_argument, 0, 'v' },1845{ "no-ftrace", no_argument, 0, 'x' },1846{ "tracefile", required_argument, 0, 'i' },1847{ "max-lat", required_argument, 0, 'm' },1848{ 0, 0, 0, 0 }1849};1850const struct policy *p;1851int max, min;1852int value;1853bool notracer, valid;18541855/*1856* We must do this before parsing the arguments because show_usage()1857* needs to display these.1858*/1859find_tracefiles();18601861while (true) {1862c = getopt_long(argc, argv, "lt:Fs:fgc:p:hnra:e:u:vxi:m:",1863long_options, &option_idx);1864if (c == -1)1865break;18661867switch (c) {1868case 'l':1869show_available();1870exit(0);1871break;1872case 't':1873current_tracer = strdup(optarg);1874if (!is_relevant_tracer(current_tracer)) {1875warnx("%s is not a known latency tracer!\n",1876current_tracer);1877}1878valid = tracer_valid(current_tracer, ¬racer);1879if (notracer)1880errx(EXIT_FAILURE, "%s", no_tracer_msg);1881if (!valid)1882errx(EXIT_FAILURE,1883"The tracer %s is not supported by your kernel!\n", current_tracer);1884break;1885case 'F':1886force_tracer = true;1887break;1888case 's':1889check_alldigits(optarg, "-s [--threshold]");1890threshold = strdup(optarg);1891break;1892case 'f':1893use_options[OPTIDX_FUNC_TR] = true;1894break;1895case 'g':1896use_options[OPTIDX_DISP_GR] = true;1897break;1898case 'c':1899p = policy_from_name(optarg);1900if (p != NULL) {1901sched_policy = p->policy;1902sched_policy_set = true;1903if (!sched_pri_set) {1904sched_pri = p->default_pri;1905sched_pri_set = true;1906}1907} else {1908warnx("Unknown scheduling %s\n", optarg);1909show_usage();1910exit(0);1911}1912break;1913case 'p':1914check_alldigits(optarg, "-p [--priority]");1915sched_pri = atoi(optarg);1916sched_pri_set = true;1917break;1918case 'h':1919show_usage();1920exit(0);1921break;1922case 'n':1923trace_enable = false;1924use_random_sleep = false;1925break;1926case 'e':1927check_alldigits(optarg, "-e [--threads]");1928value = atoi(optarg);1929if (value > 0)1930nr_threads = value;1931else {1932warnx("NRTHR must be > 0\n");1933show_usage();1934exit(0);1935}1936break;1937case 'u':1938check_alldigits(optarg, "-u [--time]");1939value = atoi(optarg);1940if (value < 0) {1941warnx("TIME must be >= 0\n");1942show_usage();1943exit(0);1944}1945trace_enable = true;1946use_random_sleep = true;1947sleep_time = value * USEC_PER_MSEC;1948break;1949case 'v':1950verbosity++;1951break;1952case 'r':1953trace_enable = true;1954use_random_sleep = true;1955break;1956case 'a':1957check_alldigits(optarg, "-a [--nrlat]");1958value = atoi(optarg);1959if (value <= 0) {1960warnx("NRLAT must be > 0\n");1961show_usage();1962exit(0);1963}1964trace_enable = true;1965use_random_sleep = true;1966table_startsize = value;1967break;1968case 'x':1969setup_ftrace = false;1970break;1971case 'i':1972setup_ftrace = false;1973debug_tracefile = strdup(optarg);1974break;1975case 'm':1976setup_ftrace = false;1977debug_maxlat = strdup(optarg);1978break;1979default:1980show_usage();1981exit(0);1982break;1983}1984}19851986if (setup_ftrace) {1987if (!current_tracer) {1988current_tracer = find_default_tracer();1989if (!current_tracer)1990errx(EXIT_FAILURE,1991"No default tracer found and tracer not specified\n");1992}19931994if (use_random_sleep && !random_makes_sense(current_tracer)) {1995warnx("WARNING: The tracer is %s and random sleep has",1996current_tracer);1997fprintf(stderr,1998"been enabled. Random sleep is intended for the following tracers:\n");1999for (i = 0; random_tracers[i]; i++)2000fprintf(stderr, "%s\n", random_tracers[i]);2001fprintf(stderr, "\n");2002}2003}20042005if (debug_tracefile == DEBUG_NOFILE ||2006debug_maxlat == DEBUG_NOFILE)2007errx(EXIT_FAILURE,2008"Could not find tracing directory e.g. /sys/kernel/tracing\n");20092010if (!sched_policy_set) {2011sched_policy = SCHED_RR;2012sched_policy_set = true;2013if (!sched_pri_set) {2014sched_pri = RT_DEFAULT_PRI;2015sched_pri_set = true;2016}2017}20182019max = sched_get_priority_max(sched_policy);2020min = sched_get_priority_min(sched_policy);20212022if (sched_pri < min) {2023printf(2024"ATTENTION: Increasing priority to minimum, which is %d\n", min);2025sched_pri = min;2026}2027if (sched_pri > max) {2028printf(2029"ATTENTION: Reducing priority to maximum, which is %d\n", max);2030sched_pri = max;2031}2032}20332034static void show_params(void)2035{2036printf(2037"\n"2038"Running with scheduling policy %s and priority %d. Using %d print threads.\n",2039policy_name(sched_policy), sched_pri, nr_threads);2040if (trace_enable) {2041if (use_random_sleep) {2042printf(2043"%s will be printed with random delay\n"2044"Start size of the probability table:\t\t\t%d\n"2045"Print a message when the prob. table changes size:\t%s\n"2046"Print a warning when an event has been lost:\t\t%s\n"2047"Sleep time is:\t\t\t\t\t\t%ld ms\n",2048debug_tracefile,2049table_startsize,2050bool2str(verbose_sizechange()),2051bool2str(verbose_lostevent()),2052sleep_time / USEC_PER_MSEC);2053} else {2054printf("%s will be printed immediately\n",2055debug_tracefile);2056}2057} else {2058printf("%s will not be printed\n",2059debug_tracefile);2060}2061if (setup_ftrace) {2062printf("Tracer:\t\t\t\t\t\t\t%s\n"2063"%s option:\t\t\t\t\t%s\n"2064"%s option:\t\t\t\t\t%s\n",2065current_tracer,2066optstr[OPTIDX_FUNC_TR],2067bool2str(use_options[OPTIDX_FUNC_TR]),2068optstr[OPTIDX_DISP_GR],2069bool2str(use_options[OPTIDX_DISP_GR]));2070if (!strcmp(threshold, "0"))2071printf("Threshold:\t\t\t\t\t\ttracing_max_latency\n");2072else2073printf("Threshold:\t\t\t\t\t\t%s\n", threshold);2074}2075printf("\n");2076}20772078int main(int argc, char *argv[])2079{2080init_save_state();2081signal_blocking(SIG_BLOCK);2082setup_sig_handler();2083open_stdout();20842085if (argc >= 1)2086prg_name = argv[0];2087else2088prg_name = prg_unknown;20892090scan_arguments(argc, argv);2091show_params();20922093init_printstate();2094init_print_mtx();2095if (use_random_sleep) {2096init_probabilities();2097if (verbose_sizechange())2098printf("Initializing probability table to %d\n",2099table_startsize);2100sleeptable_resize(table_startsize, false, NULL);2101}2102set_priority();2103init_queue(&printqueue);2104start_printthread();2105tracing_loop();2106return 0;2107}210821092110