/* * trace-collection.c * * Babeltrace Library * * Copyright 2012 EfficiOS Inc. and Linux Foundation * * Author: Mathieu Desnoyers * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include #include /* for clocks */ #include #include struct clock_match { GHashTable *clocks; struct ctf_clock *clock_match; struct trace_collection *tc; }; static void check_clock_match(gpointer key, gpointer value, gpointer user_data) { struct clock_match *match = user_data; struct ctf_clock *clock_a = value, *clock_b; if (clock_a->absolute) { /* * Absolute time references, such as NTP, are looked up * by clock name. */ clock_b = g_hash_table_lookup(match->clocks, (gpointer) (unsigned long) clock_a->name); if (clock_b) { match->clock_match = clock_b; return; } } else if (clock_a->uuid != 0) { /* * Lookup the the trace clocks into the collection * clocks. */ clock_b = g_hash_table_lookup(match->clocks, (gpointer) (unsigned long) clock_a->uuid); if (clock_b) { match->clock_match = clock_b; return; } } } static void clock_add(gpointer key, gpointer value, gpointer user_data) { struct clock_match *clock_match = user_data; GHashTable *tc_clocks = clock_match->clocks; struct ctf_clock *t_clock = value, *clock_copy; GQuark v; if (t_clock->absolute) v = t_clock->name; else v = t_clock->uuid; if (v) { struct ctf_clock *tc_clock; tc_clock = g_hash_table_lookup(tc_clocks, (gpointer) (unsigned long) v); if (!tc_clock) { /* * For now we only support CTF that has one * single clock uuid or name (absolute ref) per * trace. */ if (g_hash_table_size(tc_clocks) > 0) { fprintf(stderr, "[error] Only CTF traces with a single clock description are supported by this babeltrace version.\n"); } if (!clock_match->tc->offset_nr) { clock_match->tc->offset_first = clock_offset_ns(t_clock); clock_match->tc->delta_offset_first_sum = 0; clock_match->tc->offset_nr++; clock_match->tc->single_clock_offset_avg = clock_match->tc->offset_first; } clock_copy = g_new0(struct ctf_clock, 1); *clock_copy = *t_clock; if (t_clock->description) { clock_copy->description = g_strdup(t_clock->description); } g_hash_table_insert(tc_clocks, (gpointer) (unsigned long) v, clock_copy); } else if (!t_clock->absolute) { int64_t diff_ns; /* * For non-absolute clocks, check that the * offsets match. If not, warn the user that we * do an arbitrary choice. */ diff_ns = clock_offset_ns(tc_clock) - clock_offset_ns(t_clock); printf_debug("Clock \"%s\" offset between traces has a delta of %" PRIu64 " ns.", g_quark_to_string(tc_clock->name), diff_ns < 0 ? -diff_ns : diff_ns); if (diff_ns > 10000 || diff_ns < -10000) { fprintf(stderr, "[warning] Clock \"%s\" offset differs between traces (delta %" PRIu64 " ns). Using average.\n", g_quark_to_string(tc_clock->name), diff_ns < 0 ? -diff_ns : diff_ns); } /* Compute average */ clock_match->tc->delta_offset_first_sum += clock_offset_ns(t_clock) - clock_match->tc->offset_first; clock_match->tc->offset_nr++; clock_match->tc->single_clock_offset_avg = clock_match->tc->offset_first + (clock_match->tc->delta_offset_first_sum / clock_match->tc->offset_nr); /* Time need to use offset average */ clock_match->tc->clock_use_offset_avg = 1; } } } /* * Whenever we add a trace to the trace collection, check that we can * correlate this trace with at least one other clock in the trace and * convert the index from cycles to real time. */ int bt_trace_collection_add(struct trace_collection *tc, struct bt_trace_descriptor *trace) { if (!tc || !trace) return -EINVAL; if (!trace->clocks) return 0; if (tc->array->len > 1) { struct clock_match clock_match = { .clocks = tc->clocks, .clock_match = NULL, .tc = NULL, }; /* * With two or more traces, we need correlation info * avalable. */ g_hash_table_foreach(trace->clocks, check_clock_match, &clock_match); if (!clock_match.clock_match) { fprintf(stderr, "[error] No clocks can be correlated and multiple traces are added to the collection. If you are certain those traces can be correlated, try using \"--clock-force-correlate\".\n"); goto error; } } g_ptr_array_add(tc->array, trace); trace->collection = tc; { struct clock_match clock_match = { .clocks = tc->clocks, .clock_match = NULL, .tc = tc, }; /* * Add each clock from the trace clocks into the trace * collection clocks. */ g_hash_table_foreach(trace->clocks, clock_add, &clock_match); } return 0; error: return -EPERM; } int bt_trace_collection_remove(struct trace_collection *tc, struct bt_trace_descriptor *td) { if (!tc || !td) return -EINVAL; if (g_ptr_array_remove(tc->array, td)) { return 0; } else { return -1; } } static void clock_free(gpointer data) { struct ctf_clock *clock = data; g_free(clock->description); g_free(clock); } void bt_init_trace_collection(struct trace_collection *tc) { assert(tc); tc->array = g_ptr_array_new(); tc->clocks = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, clock_free); tc->single_clock_offset_avg = 0; tc->offset_first = 0; tc->delta_offset_first_sum = 0; tc->offset_nr = 0; } /* * bt_finalize_trace_collection() closes the opened traces for read * and free the memory allocated for trace collection */ void bt_finalize_trace_collection(struct trace_collection *tc) { assert(tc); g_ptr_array_free(tc->array, TRUE); g_hash_table_destroy(tc->clocks); }