/* *------------------------------------------------------------------ * Copyright (c) 2005-2016 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <sys/fcntl.h> #include <sys/mman.h> #include <arpa/inet.h> #include <stdio.h> #include <gtk/gtk.h> #include "g2.h" #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> /* * globals */ boolean g_little_endian; event_t *g_events; ulong g_nevents; pid_sort_t *g_pids; pid_sort_t *g_original_pids; int g_npids; pid_data_t *g_pid_data_list; /* * locals */ pid_data_t **s_pidhash; /* * config parameters */ double ticks_per_ns=1000.0; boolean ticks_per_ns_set; /**************************************************************************** * event_init ****************************************************************************/ void event_init(void) { ulong endian; char *ep; char *askstr; int tmp; ep = (char *)&endian; endian = 0x12345678; if (*ep != 0x12) g_little_endian = TRUE; else g_little_endian = FALSE; askstr = getprop("dont_ask_ticks_per_ns_initially"); if (askstr && (*askstr == 't' || *askstr == 'T')) { tmp = atol(getprop_default("ticks_per_ns", 0)); if (tmp > 0) { ticks_per_ns = tmp; ticks_per_ns_set = TRUE; } } } /**************************************************************************** * find_or_add_pid ****************************************************************************/ pid_data_t *find_or_add_pid (ulong pid) { pid_data_t *pp; ulong bucket; bucket = pid % PIDHASH_NBUCKETS; pp = s_pidhash[bucket]; if (pp == 0) { pp = g_malloc0(sizeof(pid_data_t)); pp->pid_value = pid; s_pidhash[bucket] = pp; g_npids++; return(pp); } while (pp) { if (pp->pid_value == pid) return(pp); pp = pp->next; } pp = g_malloc0(sizeof(pid_data_t)); pp->pid_value = pid; pp->next = s_pidhash[bucket]; s_pidhash[bucket] = pp; g_npids++; return(pp); } /**************************************************************************** * pid_cmp ****************************************************************************/ int pid_cmp(const void *a1, const void *a2) { pid_sort_t *p1 = (pid_sort_t *)a1; pid_sort_t *p2 = (pid_sort_t *)a2; if (p1->pid_value < p2->pid_value) return(-1); else if (p1->pid_value == p2->pid_value) return(0); else return(1); } /**************************************************************************** * make_sorted_pid_vector ****************************************************************************/ static void make_sorted_pid_vector(void) { pid_data_t *pp; pid_data_t **p_previous; pid_sort_t *psp; int i; psp = g_pids = g_malloc0(sizeof(pid_sort_t)*g_npids); for (i = 0; i < PIDHASH_NBUCKETS; i++) { pp = s_pidhash[i]; while(pp) { psp->pid = pp; psp->pid_value = pp->pid_value; psp++; pp = pp->next; } } qsort(&g_pids[0], g_npids, sizeof(pid_sort_t), pid_cmp); /* put the sort order into the pid objects */ psp = g_pids; /* * This is rather gross. * * We happen to know that whenever this function is called, the hash table * structure itself is immediately torn down. So the "next" pointers in the * pid_data_t elements are about to become useless. * * So we re-use them, to link all the pid_data_t elements together into a * single unified linked list, with g_pid_data_list pointing to the head. * This means we can walk all the pid_data_t objects if we really want to. * Reading snapshots from disk is one example. * * Alternatively we could just leave the hash table in place; this is * far nicer, but as it happens, trading O(n) lookups for O(1) lookups * isn't actually a problem for the restricted post-tear-down usage. So for * now we take the memory savings and swap our hash table for a list. */ p_previous = &g_pid_data_list; for (i = 0; i < g_npids; i++) { pp = psp->pid; pp->pid_index = i; *p_previous = pp; p_previous = &pp->next; psp++; } *p_previous = NULL; /* * Squirrel away original (sorted) vector, so we can * toggle between "chase" mode, snapshots, and the original * display method on short notice */ g_original_pids = g_malloc0(sizeof(pid_sort_t)*g_npids); memcpy (g_original_pids, g_pids, sizeof(pid_sort_t)*g_npids); } /**************************************************************************** * read_events ****************************************************************************/ void read_events(char *filename) { ulong *ulp; ulong size; event_t *ep; raw_event_t *rep; ulonglong start_time=0ULL; ulonglong low_time; boolean once=TRUE; int i; char tmpbuf [128]; ulp = (ulong *)mapfile(filename, &size); if (ulp == NULL) { sprintf(tmpbuf, "Couldn't open %s\n", filename); infobox("Read Event Log Failure", tmpbuf); return; } g_nevents = ntohl(*ulp); if (size != (g_nevents*sizeof(raw_event_t) + sizeof(g_nevents))) { sprintf(tmpbuf, "%s was damaged, or isn't an event log.\n", filename); infobox("Bad Input File", tmpbuf); g_nevents = 0; unmapfile((char *)ulp, size); return; } rep = (raw_event_t *)(ulp+1); if (g_events) g_free(g_events); g_events = (event_t *)g_malloc(g_nevents * sizeof(event_t)); ep = g_events; while (g_npids > 0) { g_free((g_pids + g_npids-1)->pid); g_npids--; } if (g_pids) { g_free(g_pids); g_free(g_original_pids); g_pids = 0; g_original_pids = 0; } s_pidhash = (pid_data_t **)g_malloc0( PIDHASH_NBUCKETS*sizeof(pid_data_t *)); /* $$$ add a SEGV handler... */ for (i = 0; i < g_nevents; i++) { if (once) { once = FALSE; start_time = ((ulonglong)ntohl(rep->time[0])); start_time <<= 32; low_time = ntohl(rep->time[1]); low_time &= 0xFFFFFFFF; start_time |= low_time; ep->time = 0LL; } else { ep->time = ((ulonglong)ntohl(rep->time[0])); ep->time <<= 32; low_time = ntohl(rep->time[1]); low_time &= 0xFFFFFFFF; ep->time |= low_time; ep->time -= start_time; ep->time /= ticks_per_ns; } ep->code = ntohl(rep->code); ep->pid = find_or_add_pid(ntohl(rep->pid)); ep->datum = ntohl(rep->datum); ep->flags = 0; ep++; rep++; } unmapfile((char *)ulp, size); make_sorted_pid_vector(); g_free(s_pidhash); s_pidhash = 0; /* Give the view-1 world a chance to reset a few things... */ view1_read_events_callback(); } static event_t *add_ep; /**************************************************************************** * cpel_event_init ****************************************************************************/ void cpel_event_init (ulong nevents) { g_nevents = nevents; if (g_events) g_free(g_events); add_ep = g_events = (event_t *)g_malloc(g_nevents * sizeof(event_t)); while (g_npids > 0) { g_free((g_pids + g_npids-1)->pid); g_npids--; } if (g_pids) { g_free(g_pids); g_free(g_original_pids); g_pids = 0; g_original_pids = 0; } s_pidhash = (pid_data_t **)g_malloc0( PIDHASH_NBUCKETS*sizeof(pid_data_t *)); } /**************************************************************************** * add_cpel_event ****************************************************************************/ void add_cpel_event(ulonglong delta, ulong track, ulong event, ulong datum) { event_t *ep; ep = add_ep++; ep->time = delta; ep->pid = find_or_add_pid(track); ep->code = event; ep->datum = datum; ep->flags = 0; } /**************************************************************************** * add_clib_event ****************************************************************************/ void add_clib_event(double delta, unsigned short track, unsigned short event, unsigned int index) { event_t *ep; ep = add_ep++; ep->time = (ulonglong) (delta * 1e9); /* time in intger nanoseconds */ ep->pid = find_or_add_pid(track); ep->code = event; ep->datum = index; ep->flags = EVENT_FLAG_CLIB; } /**************************************************************************** * cpel_event_finalize ****************************************************************************/ void cpel_event_finalize(void) { make_sorted_pid_vector(); g_free(s_pidhash); s_pidhash = 0; /* Give the view-1 world a chance to reset a few things... */ view1_read_events_callback(); } /**************************************************************************** * mapfile ****************************************************************************/ char *mapfile (char *file, ulong *sizep) { struct stat statb; char *rv; int maphfile; size_t mapfsize; maphfile = open (file, O_RDONLY); if (maphfile < 0) return (NULL); if (fstat (maphfile, &statb) < 0) { return (NULL); } /* Don't try to mmap directories, FIFOs, semaphores, etc. */ if (! (statb.st_mode & S_IFREG)) { return (NULL); } mapfsize = statb.st_size; if (mapfsize < 3) { close (maphfile); return (NULL); } rv = mmap (0, mapfsize, PROT_READ, MAP_SHARED, maphfile, 0); if (rv == 0) { g_error ("%s mapping problem, I quit...\n", file); } close (maphfile); if (madvise (rv, mapfsize, MADV_SEQUENTIAL) < 0) { return (rv); } if (sizep) { *sizep = mapfsize; } return (rv); } /**************************************************************************** * unmapfile ****************************************************************************/ boolean unmapfile (char *addr, ulong size) { if (munmap (addr, size) < 0) { g_warning("Unmap error, addr 0x%lx size 0x%x\n", (unsigned long) addr, (unsigned int)size); return(FALSE); } return(TRUE); } /**************************************************************************** * find_event_index * Binary search for first event whose time is >= t ****************************************************************************/ int find_event_index (ulonglong t) { int index, bottom, top; event_t *ep; bottom = g_nevents-1; top = 0; while (1) { index = (bottom + top) / 2; ep = (g_events + index); if (ep->time == t) return(index); if (top >= bottom) { while (index > 0 && ep->time > t) { ep--; index--; } while (index < g_nevents && ep->time < t) { ep++; index++; } return(index); } if (ep->time < t) top = index + 1; else bottom = index - 1; } } /**************************************************************************** * events_about ****************************************************************************/ void events_about (char *tmpbuf) { sprintf(tmpbuf+strlen(tmpbuf), "%d total events, %.3f ticks per us\n", (int)g_nevents, ticks_per_ns); }