Dave Barach | 52642c3 | 2016-02-11 19:28:19 -0500 | [diff] [blame] | 1 | /* |
| 2 | *------------------------------------------------------------------ |
| 3 | * Copyright (c) 2005-2016 Cisco and/or its affiliates. |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at: |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include <stdlib.h> |
| 18 | #include <unistd.h> |
| 19 | #include <sys/stat.h> |
| 20 | #include <sys/fcntl.h> |
| 21 | #include <sys/mman.h> |
| 22 | #include <arpa/inet.h> |
| 23 | #include <stdio.h> |
| 24 | #include <gtk/gtk.h> |
| 25 | #include "g2.h" |
| 26 | #include <sys/types.h> |
| 27 | #include <sys/stat.h> |
| 28 | #include <fcntl.h> |
| 29 | #include <string.h> |
| 30 | |
| 31 | /* |
| 32 | * globals |
| 33 | */ |
| 34 | boolean g_little_endian; |
| 35 | event_t *g_events; |
| 36 | ulong g_nevents; |
| 37 | pid_sort_t *g_pids; |
| 38 | pid_sort_t *g_original_pids; |
| 39 | int g_npids; |
| 40 | pid_data_t *g_pid_data_list; |
| 41 | |
| 42 | /* |
| 43 | * locals |
| 44 | */ |
| 45 | pid_data_t **s_pidhash; |
| 46 | |
| 47 | /* |
| 48 | * config parameters |
| 49 | */ |
| 50 | |
| 51 | double ticks_per_ns=1000.0; |
| 52 | boolean ticks_per_ns_set; |
| 53 | |
| 54 | /**************************************************************************** |
| 55 | * event_init |
| 56 | ****************************************************************************/ |
| 57 | |
| 58 | void event_init(void) |
| 59 | { |
| 60 | ulong endian; |
| 61 | char *ep; |
| 62 | char *askstr; |
| 63 | int tmp; |
| 64 | |
| 65 | ep = (char *)&endian; |
| 66 | endian = 0x12345678; |
| 67 | if (*ep != 0x12) |
| 68 | g_little_endian = TRUE; |
| 69 | else |
| 70 | g_little_endian = FALSE; |
| 71 | |
| 72 | askstr = getprop("dont_ask_ticks_per_ns_initially"); |
| 73 | |
| 74 | if (askstr && (*askstr == 't' || *askstr == 'T')) { |
| 75 | tmp = atol(getprop_default("ticks_per_ns", 0)); |
| 76 | if (tmp > 0) { |
| 77 | ticks_per_ns = tmp; |
| 78 | ticks_per_ns_set = TRUE; |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | /**************************************************************************** |
| 84 | * find_or_add_pid |
| 85 | ****************************************************************************/ |
| 86 | |
| 87 | pid_data_t *find_or_add_pid (ulong pid) |
| 88 | { |
| 89 | pid_data_t *pp; |
| 90 | ulong bucket; |
| 91 | |
| 92 | bucket = pid % PIDHASH_NBUCKETS; |
| 93 | |
| 94 | pp = s_pidhash[bucket]; |
| 95 | |
| 96 | if (pp == 0) { |
| 97 | pp = g_malloc0(sizeof(pid_data_t)); |
| 98 | pp->pid_value = pid; |
| 99 | s_pidhash[bucket] = pp; |
| 100 | g_npids++; |
| 101 | return(pp); |
| 102 | } |
| 103 | while (pp) { |
| 104 | if (pp->pid_value == pid) |
| 105 | return(pp); |
| 106 | pp = pp->next; |
| 107 | } |
| 108 | |
| 109 | pp = g_malloc0(sizeof(pid_data_t)); |
| 110 | pp->pid_value = pid; |
| 111 | pp->next = s_pidhash[bucket]; |
| 112 | s_pidhash[bucket] = pp; |
| 113 | g_npids++; |
| 114 | return(pp); |
| 115 | } |
| 116 | |
| 117 | /**************************************************************************** |
| 118 | * pid_cmp |
| 119 | ****************************************************************************/ |
| 120 | |
| 121 | int pid_cmp(const void *a1, const void *a2) |
| 122 | { |
| 123 | pid_sort_t *p1 = (pid_sort_t *)a1; |
| 124 | pid_sort_t *p2 = (pid_sort_t *)a2; |
| 125 | |
| 126 | if (p1->pid_value < p2->pid_value) |
| 127 | return(-1); |
| 128 | else if (p1->pid_value == p2->pid_value) |
| 129 | return(0); |
| 130 | else |
| 131 | return(1); |
| 132 | } |
| 133 | |
| 134 | /**************************************************************************** |
| 135 | * make_sorted_pid_vector |
| 136 | ****************************************************************************/ |
| 137 | |
| 138 | static void make_sorted_pid_vector(void) |
| 139 | { |
| 140 | pid_data_t *pp; |
| 141 | pid_data_t **p_previous; |
| 142 | pid_sort_t *psp; |
| 143 | int i; |
| 144 | |
Dave Barach | a8ed6bd | 2017-04-04 08:00:23 -0400 | [diff] [blame] | 145 | psp = g_pids = g_malloc0(sizeof(pid_sort_t)*g_npids); |
Dave Barach | 52642c3 | 2016-02-11 19:28:19 -0500 | [diff] [blame] | 146 | |
| 147 | for (i = 0; i < PIDHASH_NBUCKETS; i++) { |
| 148 | pp = s_pidhash[i]; |
| 149 | while(pp) { |
| 150 | psp->pid = pp; |
| 151 | psp->pid_value = pp->pid_value; |
| 152 | psp++; |
| 153 | pp = pp->next; |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | qsort(&g_pids[0], g_npids, sizeof(pid_sort_t), pid_cmp); |
| 158 | |
| 159 | /* put the sort order into the pid objects */ |
| 160 | psp = g_pids; |
| 161 | |
| 162 | /* |
| 163 | * This is rather gross. |
| 164 | * |
| 165 | * We happen to know that whenever this function is called, the hash table |
| 166 | * structure itself is immediately torn down. So the "next" pointers in the |
| 167 | * pid_data_t elements are about to become useless. |
| 168 | * |
| 169 | * So we re-use them, to link all the pid_data_t elements together into a |
| 170 | * single unified linked list, with g_pid_data_list pointing to the head. |
| 171 | * This means we can walk all the pid_data_t objects if we really want to. |
| 172 | * Reading snapshots from disk is one example. |
| 173 | * |
| 174 | * Alternatively we could just leave the hash table in place; this is |
| 175 | * far nicer, but as it happens, trading O(n) lookups for O(1) lookups |
| 176 | * isn't actually a problem for the restricted post-tear-down usage. So for |
| 177 | * now we take the memory savings and swap our hash table for a list. |
| 178 | */ |
| 179 | p_previous = &g_pid_data_list; |
| 180 | for (i = 0; i < g_npids; i++) { |
| 181 | pp = psp->pid; |
| 182 | pp->pid_index = i; |
| 183 | *p_previous = pp; |
| 184 | p_previous = &pp->next; |
| 185 | psp++; |
| 186 | } |
| 187 | *p_previous = NULL; |
| 188 | |
| 189 | /* |
| 190 | * Squirrel away original (sorted) vector, so we can |
| 191 | * toggle between "chase" mode, snapshots, and the original |
| 192 | * display method on short notice |
| 193 | */ |
Dave Barach | a8ed6bd | 2017-04-04 08:00:23 -0400 | [diff] [blame] | 194 | g_original_pids = g_malloc0(sizeof(pid_sort_t)*g_npids); |
Dave Barach | 52642c3 | 2016-02-11 19:28:19 -0500 | [diff] [blame] | 195 | memcpy (g_original_pids, g_pids, sizeof(pid_sort_t)*g_npids); |
| 196 | } |
| 197 | |
| 198 | /**************************************************************************** |
| 199 | * read_events |
| 200 | ****************************************************************************/ |
| 201 | |
| 202 | void read_events(char *filename) |
| 203 | { |
| 204 | ulong *ulp; |
| 205 | ulong size; |
| 206 | event_t *ep; |
| 207 | raw_event_t *rep; |
| 208 | ulonglong start_time=0ULL; |
| 209 | ulonglong low_time; |
| 210 | boolean once=TRUE; |
| 211 | int i; |
| 212 | char tmpbuf [128]; |
| 213 | |
| 214 | ulp = (ulong *)mapfile(filename, &size); |
| 215 | |
| 216 | if (ulp == NULL) { |
| 217 | sprintf(tmpbuf, "Couldn't open %s\n", filename); |
| 218 | infobox("Read Event Log Failure", tmpbuf); |
| 219 | return; |
| 220 | } |
| 221 | |
| 222 | g_nevents = ntohl(*ulp); |
| 223 | |
| 224 | if (size != (g_nevents*sizeof(raw_event_t) + sizeof(g_nevents))) { |
| 225 | sprintf(tmpbuf, "%s was damaged, or isn't an event log.\n", filename); |
| 226 | infobox("Bad Input File", tmpbuf); |
| 227 | g_nevents = 0; |
| 228 | unmapfile((char *)ulp, size); |
| 229 | return; |
| 230 | } |
| 231 | |
| 232 | rep = (raw_event_t *)(ulp+1); |
| 233 | |
| 234 | if (g_events) |
| 235 | g_free(g_events); |
| 236 | |
| 237 | g_events = (event_t *)g_malloc(g_nevents * sizeof(event_t)); |
| 238 | ep = g_events; |
| 239 | |
| 240 | while (g_npids > 0) { |
| 241 | g_free((g_pids + g_npids-1)->pid); |
| 242 | g_npids--; |
| 243 | } |
| 244 | if (g_pids) { |
| 245 | g_free(g_pids); |
| 246 | g_free(g_original_pids); |
| 247 | g_pids = 0; |
| 248 | g_original_pids = 0; |
| 249 | } |
| 250 | |
| 251 | s_pidhash = (pid_data_t **)g_malloc0( |
| 252 | PIDHASH_NBUCKETS*sizeof(pid_data_t *)); |
| 253 | |
| 254 | /* $$$ add a SEGV handler... */ |
| 255 | for (i = 0; i < g_nevents; i++) { |
| 256 | if (once) { |
| 257 | once = FALSE; |
| 258 | start_time = ((ulonglong)ntohl(rep->time[0])); |
| 259 | start_time <<= 32; |
| 260 | low_time = ntohl(rep->time[1]); |
| 261 | low_time &= 0xFFFFFFFF; |
| 262 | start_time |= low_time; |
| 263 | ep->time = 0LL; |
| 264 | } else { |
| 265 | ep->time = ((ulonglong)ntohl(rep->time[0])); |
| 266 | ep->time <<= 32; |
| 267 | low_time = ntohl(rep->time[1]); |
| 268 | low_time &= 0xFFFFFFFF; |
| 269 | ep->time |= low_time; |
| 270 | ep->time -= start_time; |
| 271 | ep->time /= ticks_per_ns; |
| 272 | } |
| 273 | ep->code = ntohl(rep->code); |
| 274 | ep->pid = find_or_add_pid(ntohl(rep->pid)); |
| 275 | ep->datum = ntohl(rep->datum); |
| 276 | ep->flags = 0; |
| 277 | ep++; |
| 278 | rep++; |
| 279 | } |
| 280 | |
| 281 | unmapfile((char *)ulp, size); |
| 282 | |
| 283 | make_sorted_pid_vector(); |
| 284 | g_free(s_pidhash); |
| 285 | s_pidhash = 0; |
| 286 | |
| 287 | /* Give the view-1 world a chance to reset a few things... */ |
| 288 | view1_read_events_callback(); |
| 289 | } |
| 290 | |
| 291 | static event_t *add_ep; |
| 292 | |
| 293 | /**************************************************************************** |
| 294 | * cpel_event_init |
| 295 | ****************************************************************************/ |
| 296 | void cpel_event_init (ulong nevents) |
| 297 | { |
| 298 | g_nevents = nevents; |
| 299 | if (g_events) |
| 300 | g_free(g_events); |
| 301 | add_ep = g_events = (event_t *)g_malloc(g_nevents * sizeof(event_t)); |
| 302 | while (g_npids > 0) { |
| 303 | g_free((g_pids + g_npids-1)->pid); |
| 304 | g_npids--; |
| 305 | } |
| 306 | if (g_pids) { |
| 307 | g_free(g_pids); |
| 308 | g_free(g_original_pids); |
| 309 | g_pids = 0; |
| 310 | g_original_pids = 0; |
| 311 | } |
| 312 | s_pidhash = (pid_data_t **)g_malloc0( |
| 313 | PIDHASH_NBUCKETS*sizeof(pid_data_t *)); |
| 314 | } |
| 315 | |
| 316 | /**************************************************************************** |
| 317 | * add_cpel_event |
| 318 | ****************************************************************************/ |
| 319 | |
| 320 | void add_cpel_event(ulonglong delta, ulong track, ulong event, ulong datum) |
| 321 | { |
| 322 | event_t *ep; |
| 323 | |
| 324 | ep = add_ep++; |
| 325 | ep->time = delta; |
| 326 | ep->pid = find_or_add_pid(track); |
| 327 | ep->code = event; |
| 328 | ep->datum = datum; |
| 329 | ep->flags = 0; |
| 330 | } |
| 331 | |
| 332 | /**************************************************************************** |
| 333 | * add_clib_event |
| 334 | ****************************************************************************/ |
| 335 | |
| 336 | void add_clib_event(double delta, unsigned short track, |
| 337 | unsigned short event, unsigned int index) |
| 338 | { |
| 339 | event_t *ep; |
| 340 | |
| 341 | ep = add_ep++; |
Paul Vinciguerra | 8feeaff | 2019-03-27 11:25:48 -0700 | [diff] [blame] | 342 | ep->time = (ulonglong) (delta * 1e9); /* time in integer nanoseconds */ |
Dave Barach | 52642c3 | 2016-02-11 19:28:19 -0500 | [diff] [blame] | 343 | ep->pid = find_or_add_pid(track); |
| 344 | ep->code = event; |
| 345 | ep->datum = index; |
| 346 | ep->flags = EVENT_FLAG_CLIB; |
| 347 | } |
| 348 | |
| 349 | /**************************************************************************** |
| 350 | * cpel_event_finalize |
| 351 | ****************************************************************************/ |
| 352 | |
| 353 | void cpel_event_finalize(void) |
| 354 | { |
| 355 | make_sorted_pid_vector(); |
| 356 | g_free(s_pidhash); |
| 357 | s_pidhash = 0; |
| 358 | |
| 359 | /* Give the view-1 world a chance to reset a few things... */ |
| 360 | view1_read_events_callback(); |
| 361 | } |
| 362 | |
| 363 | /**************************************************************************** |
| 364 | * mapfile |
| 365 | ****************************************************************************/ |
| 366 | |
| 367 | char *mapfile (char *file, ulong *sizep) |
| 368 | { |
| 369 | struct stat statb; |
| 370 | char *rv; |
| 371 | int maphfile; |
| 372 | size_t mapfsize; |
| 373 | |
| 374 | maphfile = open (file, O_RDONLY); |
| 375 | |
| 376 | if (maphfile < 0) |
| 377 | return (NULL); |
| 378 | |
| 379 | if (fstat (maphfile, &statb) < 0) { |
| 380 | return (NULL); |
| 381 | } |
| 382 | |
| 383 | /* Don't try to mmap directories, FIFOs, semaphores, etc. */ |
| 384 | if (! (statb.st_mode & S_IFREG)) { |
| 385 | return (NULL); |
| 386 | } |
| 387 | |
| 388 | mapfsize = statb.st_size; |
| 389 | |
| 390 | if (mapfsize < 3) { |
| 391 | close (maphfile); |
| 392 | return (NULL); |
| 393 | } |
| 394 | |
| 395 | rv = mmap (0, mapfsize, PROT_READ, MAP_SHARED, maphfile, 0); |
| 396 | |
| 397 | if (rv == 0) { |
| 398 | g_error ("%s mapping problem, I quit...\n", file); |
| 399 | } |
| 400 | |
| 401 | close (maphfile); |
| 402 | |
| 403 | if (madvise (rv, mapfsize, MADV_SEQUENTIAL) < 0) { |
| 404 | return (rv); |
| 405 | } |
| 406 | |
| 407 | if (sizep) { |
| 408 | *sizep = mapfsize; |
| 409 | } |
| 410 | return (rv); |
| 411 | } |
| 412 | |
| 413 | /**************************************************************************** |
| 414 | * unmapfile |
| 415 | ****************************************************************************/ |
| 416 | |
| 417 | boolean unmapfile (char *addr, ulong size) |
| 418 | { |
| 419 | if (munmap (addr, size) < 0) { |
| 420 | g_warning("Unmap error, addr 0x%lx size 0x%x\n", |
| 421 | (unsigned long) addr, (unsigned int)size); |
| 422 | return(FALSE); |
| 423 | } |
| 424 | return(TRUE); |
| 425 | } |
| 426 | |
| 427 | /**************************************************************************** |
| 428 | * find_event_index |
| 429 | * Binary search for first event whose time is >= t |
| 430 | ****************************************************************************/ |
| 431 | |
| 432 | int find_event_index (ulonglong t) |
| 433 | { |
| 434 | int index, bottom, top; |
| 435 | event_t *ep; |
| 436 | |
| 437 | bottom = g_nevents-1; |
| 438 | top = 0; |
| 439 | |
| 440 | while (1) { |
| 441 | index = (bottom + top) / 2; |
| 442 | |
| 443 | ep = (g_events + index); |
| 444 | |
| 445 | if (ep->time == t) |
| 446 | return(index); |
| 447 | |
| 448 | if (top >= bottom) { |
| 449 | while (index > 0 && ep->time > t) { |
| 450 | ep--; |
| 451 | index--; |
| 452 | } |
| 453 | while (index < g_nevents && ep->time < t) { |
| 454 | ep++; |
| 455 | index++; |
| 456 | } |
| 457 | return(index); |
| 458 | } |
| 459 | |
| 460 | if (ep->time < t) |
| 461 | top = index + 1; |
| 462 | else |
| 463 | bottom = index - 1; |
| 464 | } |
| 465 | } |
| 466 | |
| 467 | /**************************************************************************** |
| 468 | * events_about |
| 469 | ****************************************************************************/ |
| 470 | |
| 471 | void events_about (char *tmpbuf) |
| 472 | { |
| 473 | sprintf(tmpbuf+strlen(tmpbuf), "%d total events, %.3f ticks per us\n", |
| 474 | (int)g_nevents, ticks_per_ns); |
| 475 | } |