1libtracecmd(3) 2============= 3 4NAME 5---- 6tracecmd_iterate_events, tracecmd_iterate_events_multi, tracecmd_follow_event, 7tracecmd_follow_missed_events, tracecmd_filter_add, tracecmd_iterate_reset - Read events from a trace file 8 9SYNOPSIS 10-------- 11[verse] 12-- 13*#include <trace-cmd.h>* 14 15int *tracecmd_iterate_events*(struct tracecmd_input pass:[*]_handle_, 16 cpu_set_t pass:[*]_cpus_, int _cpu_size_, 17 int (pass:[*]_callback_)(struct tracecmd_input pass:[*], 18 struct tep_record pass:[*], 19 int, void pass:[*]), 20 void pass:[*]_callback_data_); 21int *tracecmd_iterate_events_multi*(struct tracecmd_input pass:[**]_handles_, 22 int _nr_handles_, 23 int (pass:[*]_callback_)(struct tracecmd_input pass:[*], 24 struct tep_record pass:[*], 25 int, void pass:[*]), 26 void pass:[*]_callback_data_); 27int *tracecmd_iterate_events_reverse*(struct tracecmd_input pass:[*]_handle_, 28 cpu_set_t pass:[*]_cpus_, int _cpu_size_, 29 int (pass:[*]_callback_)(struct tracecmd_input pass:[*], 30 struct tep_record pass:[*], 31 int, void pass:[*]), 32 void pass:[*]_callback_data_, bool _cont_); 33int *tracecmd_follow_event*(struct tracecmd_input pass:[*]_handle_, 34 const char pass:[*]_system_, const char pass:[*]_event_name_, 35 int (pass:[*]_callback_)(struct tracecmd_input pass:[*], 36 struct tep_event pass:[*], 37 struct tep_record pass:[*], 38 int, void pass:[*]), 39 void pass:[*]_callback_data_); 40int *tracecmd_follow_missed_events*(struct tracecmd_input pass:[*]_handle_, 41 int (pass:[*]_callback_)(struct tracecmd_input pass:[*], 42 struct tep_event pass:[*], 43 struct tep_record pass:[*], 44 int, void pass:[*]), 45 void pass:[*]_callback_data_); 46struct tracecmd_filter pass:[*]*tracecmd_filter_add*(struct tracecmd_input *_handle_, 47 const char pass:[*]_filter_str_, bool _neg_); 48int *tracecmd_iterate_reset*(struct tracecmd_input pass:[*]_handle_); 49-- 50 51DESCRIPTION 52----------- 53This set of APIs can be used to iterate over events after opening a trace file 54using one of the open functions like *tracecmd_open(3)* or *tracecmd_open_fd(3)*. 55 56The function *tracecmd_iterate_events()* will iterate through all the 57events in the trace file defined by _handle_, where _handle_ is returned from 58one of the *tracecmd_open(3)* functions. It will call the _callback_() function 59on the events on the CPUs defined by _cpus_. The _cpu_size_ must be the size of 60_cpus_ (see *CPU_SET(3)*). If _cpus_ is NULL, then _cpu_size_ is ignored and _callback()_ 61will be called for all events on all CPUs in the trace file. The _callback_data_ 62is passed to the _callback()_ as its last parameter. _callback_ may be NULL, which 63is useful if *tracecmd_follow_event()* is used, but note if _callback_ is NULL, then 64_callback_data_ is ignored and not sent to the _callback_ of *tracecmd_follow_event()*. 65 66The function *tracecmd_iterate_events_multi()* is similar to *tracecmd_iterate_events()* 67except that it allows to iterate over more than one trace file. If *tracecmd agent(1)* 68is used to get a trace file for both the host and guest, make sure that the host trace 69file is the first entry in _handles_ and *tracecmd_iterate_events_multi()* will do 70the synchronization of the meta data for the guest files that come later in _handles_. 71_handles_ is an array of trace file descriptors that were opened by *tracecmd_open(3)* 72and friends. Note, unlike *tracecmd_iterate_events()*, *tracecmd_iterate_events_multi()* 73does not filter on CPUs, as it will cause the API to become too complex in knowing which 74handle to filter the CPUs on. If CPU filtering is desired, then the _callback_ should check 75the _record_->cpu to and return 0 if it is not the desired CPU to process. _nr_handles_ 76denotes the number of elements in _handles_. The _callback_data_ is passed to the _callback_ 77as its last parameter. _callback_ may be NULL, which is useful if *tracecmd_follow_event()* 78is used, but note if _callback_ is NULL, then _callback_data_ is ignored and not sent to the 79_callback_ of *tracecmd_follow_event()*. 80 81The function *tracecmd_iterate_events_reverse()* works pretty much the same way as 82*tracecmd_iterate_events()* works, but instead of calling the _callback_() function for 83each event in order of the timestamp, it will call the _callback_() function for 84each event in reverse order of the timestamp. If _cont_ is false, it will start by 85calling the event with the oldest timestamp in the trace.dat file. If _cont_ is set 86to true, then it will start whereever the current position of the tracing data is. 87For instance, if the _callback()_ return something other than zero it will exit the 88iteration. If *tracecmd_iterate_events_reverse()* is called again with _cont_ to true 89it will continue where it left off. If _cont_ is false, it will start again at the event 90with the oldest timestamp. The _handle_, _cpus_, _cpu_size_, and _callback_data_ act the 91same as *tracecmd_iterate_events()*. 92 93The _callback()_ for both *tracecmd_iterate_events()*, *tracecmd_iterate_events_reverse()* 94and *tracecmd_iterate_events_multi()* is of the prototype: 95 96int _callback()_(struct tracecmd_input pass:[*]_handle_, struct tep_record pass:[*]_record_, 97 int _cpu_, void pass:[*]_data_); 98 99The _handle_ is the same _handle_ passed to *tracecmd_iterate_events()* or the current 100handle of _handles_ passed to *tracecmd_iterate_events_multi()* that the _record_ belongs to. 101The _record_ is the current event record. The _cpu_ is the current CPU being processed. Note, for 102*tracecmd_iterate_events_multi()* it may not be the actual CPU of the file, but the nth 103CPU of all the _handles_ put together. Use _record_->cpu to get the actual CPU that the 104event is on. 105 106The *tracecmd_follow_event()* function will attach to a trace file descriptor _handle_ 107and call the _callback_ when the event described by _system_ and _name_ matches an event 108in the iteration of *tracecmd_iterate_events()* or *tracecmd_iterate_events_multi()*. 109Note, the _cpu_ is the nth CPU for both *tracecmd_iterate_events()* and 110*tracecmd_iterate_events_multi()*. If the actual CPU of the _record_ is needed, use 111_record_->cpu. 112For *tracecmd_iterate_events_multi()*, the _callback_ is only called if the _handle_ 113matches the current trace file descriptor within _handles_. The _callback_data_ is 114passed as the last parameter to the _callback()_ function. Note, this _callback()_ 115function will be called before the _callback()_ function of either *tracecmd_iterate_events()* or *tracecmd_iterate_events_multi()*. 116 117The _callback()_ prototype for *tracecmd_follow_event()_ is: 118 119int _callback()_(struct tracecmd_input pass:[*]_handle_, struct tep_event pass:[*]_event, 120 struct tep_record pass:[*]_record_, int _cpu_, void pass:[*]_data_); 121 122The *tracecmd_follow_missed_events()* function will attach to a trace file descriptor 123_handle_ and call the _callback_ when missed events are detected. The _event_ will 124hold the type of event that the _record_ is. The _record_ will hold the information 125of the missed events. The _cpu_ is the nth CPU for both *tracecmd_iterate_events()* 126and *tracecmd_iterate_events_multi()*. If the CPU that the missed events are for is 127needed, use _record_->cpu. If _record_->missed_events is a positive number, then it 128holds the number of missed events since the last event on its CPU, otherwise it 129will be negative, and that will mean that the number of missed events is unknown but 130missed events exist since the last event on the CPU. 131The _callback_ and _callback_data_ is the same format as *tracecmd_follow_event()* above. 132The missed events _callback_ is called before any of the other _callbacks_ and any 133filters that were added by *tracecmd_filter_add()* are ignored. 134If _callback_ returns a non zero, it will stop the iterator before it calls any of the 135other iterator callbacks for the given record. 136 137The *tracecmd_filter_add()* function, adds a filter to _handle_ that affects 138both *tracecmd_iterate_events()* and *tracecmd_iterate_events_multi()*. 139The _filter_str_ is a character string defining a filter in a format that 140is defined by *tep_filter_add_filter_str(3)*. If _neg_ is true, then the events 141that match the filter will be skipped, otherwise the events that match will execute 142the _callback()_ function in the iterators. 143 144The *tracecmd_iterate_reset()* sets the _handle_ back to start at the beginning, so that 145the next call to *tracecmd_iterate_events()* starts back at the first event again, instead 146of continuing where it left off. 147 148RETURN VALUE 149------------ 150Both *tracecmd_iterate_events()*, *tracecmd_iterate_events_reverse()* and 151*tracecmd_iterate_events_multi()* return zero if they successfully iterated all events 152(handling the follow and filters appropriately). Or an error value, which can include 153returning a non-zero result from the _callback()_ function. 154 155*tracecmd_iterate_reset()* returns 0 on success and -1 if an error occurred. Note, 156if -1 is returned, a partial reset may have also happened. 157 158EXAMPLE 159------- 160[source,c] 161-- 162#define _GNU_SOURCE 163#include <sched.h> 164#include <stdlib.h> 165#include <getopt.h> 166#include <trace-cmd.h> 167 168struct private_data { 169 int cpu; 170 const char *file; 171}; 172 173static int print_events(struct tracecmd_input *handle, struct tep_record *record, int cpu, void *data) 174{ 175 static struct trace_seq seq; 176 struct tep_handle *tep = tracecmd_get_tep(handle); 177 struct private_data *pdata = tracecmd_get_private(handle); 178 179 /* For multi handles we need this */ 180 if (pdata->cpu >= 0 && pdata->cpu != record->cpu) 181 return 0; 182 183 if (!seq.buffer) 184 trace_seq_init(&seq); 185 186 trace_seq_reset(&seq); 187 trace_seq_printf(&seq, "%s: ", pdata->file); 188 tep_print_event(tep, &seq, record, "%6.1000d [%03d] %s-%d %s: %s\n", 189 TEP_PRINT_TIME, TEP_PRINT_CPU, TEP_PRINT_COMM, TEP_PRINT_PID, 190 TEP_PRINT_NAME, TEP_PRINT_INFO); 191 trace_seq_terminate(&seq); 192 trace_seq_do_printf(&seq); 193 return 0; 194} 195 196static int print_event(struct tracecmd_input *handle, struct tep_event *event, 197 struct tep_record *record, int cpu, void *data) 198{ 199 return print_events(handle, record, cpu, data); 200} 201 202static int missed_events(struct tracecmd_input *handle, struct tep_event *event, 203 struct tep_record *record, int cpu, void *data) 204{ 205 if (record->missed_events > 0) 206 printf("CPU [%03d] has %d missed events\n", 207 record->cpu, record->missed_events); 208 else 209 printf("CPU [%03d] has missed events\n", record->cpu); 210 return 0; 211} 212 213static void usage(const char *argv0) 214{ 215 printf("usage: [-c cpu][-f filter][-e event] %s trace.dat [trace.dat ...]\n", 216 argv0); 217 exit(-1); 218} 219 220int main(int argc, char **argv) 221{ 222 struct tracecmd_input **handles = NULL; 223 const char *filter_str = NULL; 224 const char *argv0 = argv[0]; 225 struct private_data *priv; 226 cpu_set_t *cpuset = NULL; 227 char *event = NULL; 228 size_t cpusize = 0; 229 int nr_handles = 0; 230 int cpu = -1; 231 int i; 232 int c; 233 234 while ((c = getopt(argc, argv, "c:f:e:")) >= 0) { 235 switch (c) { 236 case 'c': 237 /* filter all trace data to this one CPU. */ 238 cpu = atoi(optarg); 239 break; 240 case 'f': 241 filter_str = optarg; 242 break; 243 case 'e': 244 event = optarg; 245 break; 246 default: 247 usage(argv0); 248 } 249 } 250 argc -= optind; 251 argv += optind; 252 253 if (argc == 0) 254 usage(argv0); 255 256 for (i = 0; i < argc; i++) { 257 handles = realloc(handles, sizeof(*handles) * (nr_handles + 1)); 258 if (!handles) 259 exit(-1); 260 handles[nr_handles] = tracecmd_open(argv[i], 0); 261 if (!handles[nr_handles]) { 262 perror(argv[i]); 263 exit(-1); 264 } 265 if (filter_str) { 266 if (tracecmd_filter_add(handles[nr_handles], filter_str, false) == NULL) { 267 perror("adding filter"); 268 exit(-1); 269 } 270 } 271 priv = calloc(1, sizeof(*priv)); 272 if (!priv) 273 exit(-1); 274 priv->file = argv[i]; 275 priv->cpu = cpu; 276 tracecmd_set_private(handles[nr_handles], priv); 277 if (event) { 278 if (tracecmd_follow_event(handles[nr_handles], NULL, event, print_event, NULL) < 0) { 279 printf("Could not follow event %s for file %s\n", event, argv[i]); 280 exit(-1); 281 } 282 } 283 tracecmd_follow_missed_events(handles[nr_handles], missed_events, NULL); 284 nr_handles++; 285 } 286 287 /* Shortcut */ 288 if (nr_handles == 1) { 289 if (cpu >= 0) { 290 cpuset = CPU_ALLOC(cpu + 1); 291 if (!cpuset) 292 exit(-1); 293 cpusize = CPU_ALLOC_SIZE(cpu + 1); 294 CPU_SET_S(cpu, cpusize, cpuset); 295 } 296 if (event) 297 tracecmd_iterate_events(handles[0], cpuset, cpusize, NULL, NULL); 298 else 299 tracecmd_iterate_events(handles[0], cpuset, cpusize, print_events, NULL); 300 } else { 301 if (event) 302 tracecmd_iterate_events_multi(handles, nr_handles, NULL, NULL); 303 else 304 tracecmd_iterate_events_multi(handles, nr_handles, print_events, NULL); 305 } 306 307 for (i = 0; i < nr_handles; i++) { 308 priv = tracecmd_get_private(handles[i]); 309 free(priv); 310 tracecmd_close(handles[i]); 311 } 312 free(handles); 313} 314-- 315FILES 316----- 317[verse] 318-- 319*trace-cmd.h* 320 Header file to include in order to have access to the library APIs. 321*-ltracecmd* 322 Linker switch to add when building a program that uses the library. 323-- 324 325SEE ALSO 326-------- 327*libtracefs(3)*, 328*libtraceevent(3)*, 329*trace-cmd(1)* 330*trace-cmd.dat(5)* 331 332AUTHOR 333------ 334[verse] 335-- 336*Steven Rostedt* <rostedt@goodmis.org> 337*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com> 338-- 339REPORTING BUGS 340-------------- 341Report bugs to <linux-trace-devel@vger.kernel.org> 342 343LICENSE 344------- 345libtracecmd is Free Software licensed under the GNU LGPL 2.1 346 347RESOURCES 348--------- 349https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/ 350 351COPYING 352------- 353Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under 354the terms of the GNU Public License (GPL). 355