]> git.saurik.com Git - apple/system_cmds.git/blobdiff - msa/LiveTraceAction.cpp
system_cmds-643.30.1.tar.gz
[apple/system_cmds.git] / msa / LiveTraceAction.cpp
diff --git a/msa/LiveTraceAction.cpp b/msa/LiveTraceAction.cpp
new file mode 100644 (file)
index 0000000..0989e45
--- /dev/null
@@ -0,0 +1,184 @@
+//
+//  LiveTraceAction.cpp
+//  msa
+//
+//  Created by James McIlree on 2/4/14.
+//  Copyright (c) 2014 Apple. All rights reserved.
+//
+
+#include "global.h"
+
+// Force materialization of the ring buffer print methods,
+// so they can be called from the debugger.
+template class EventRingBuffer<Kernel32>;
+template class EventRingBuffer<Kernel64>;
+
+static bool shouldProcessEvents;
+static uint32_t sigintCount;
+
+static bool start_live_tracing(Globals& globals)
+{
+       if (!KDBG::reset()) return false;
+       if (!KDBG::set_buffer_capacity(globals.trace_buffer_size())) return false;
+       if (!KDBG::set_nowrap(false)) return false;
+       if (!KDBG::initialize_buffers()) return false;
+       if (!KDBG::set_enabled(KDEBUG_ENABLE_TRACE)) return false;
+
+       return true;
+}
+
+static void end_live_tracing(void)
+{
+       KDBG::reset();
+}
+
+static void signal_handler_ctrl_C(int sig)
+{
+       shouldProcessEvents = false;
+       if (++sigintCount >= 5) {
+               // Not responding, nuke it from orbit.
+               exit(1);
+       }
+}
+
+template <typename SIZE>
+static void live_trace_event_loop(Globals& globals)
+{
+       // Handle ctrl-C
+       shouldProcessEvents = true;
+       sigintCount = 0;
+       
+       while (shouldProcessEvents) {
+               signal(SIGINT, signal_handler_ctrl_C);
+
+               EventRingBuffer<SIZE> ring_buffer(globals, globals.trace_buffer_size() * 2);
+
+               {
+                       char buf[PATH_MAX];
+                       char* buf_end = buf + sizeof(buf);
+                       print_mach_msg_header(buf, buf_end, globals);
+                       dprintf(globals.output_fd(), "%s", buf);
+               }
+
+               VoucherContentSysctl contents(globals.should_trace_voucher_contents());
+               
+               if (start_live_tracing(globals)) {
+
+                       // Okay, our goal is to hit specific timeposts.
+                       // IOW, if our target is every 10ms, and we spend 3ms doing work,
+                       // we sleep 7ms.
+                       AbsTime traceUpdateIntervalAbs = globals.live_update_interval();
+                       AbsTime now, next_trace_update = AbsTime::now();
+                       std::unique_ptr<Machine<SIZE>> machine, last_machine;
+
+                       std::unordered_map<pid_t, bool> task_appnap_state;
+                       std::unordered_map<pid_t, TaskRequestedPolicy> task_requested_state;
+                       std::unordered_map<typename SIZE::ptr_t, TaskRequestedPolicy> thread_requested_state;
+                       std::unordered_map<pid_t, std::pair<TaskEffectivePolicy, uint32_t>> task_effective_state;
+                       std::unordered_map<typename SIZE::ptr_t, std::pair<TaskEffectivePolicy, uint32_t>> thread_effective_state;
+                       std::unordered_map<pid_t, std::pair<uint32_t, uint32_t>> task_boosts;
+
+                       while (shouldProcessEvents) {
+                               now = AbsTime::now();
+                               if (now >= next_trace_update) {
+                                       std::size_t count, capacity;
+                                       KDEvent<SIZE>* events;
+
+                                       std::tie(events, count, capacity) = ring_buffer.read();
+                                       if (count) {
+                                               if (last_machine) {
+                                                       machine = std::make_unique<Machine<SIZE>>(*last_machine, events, count);
+                                               } else {
+                                                       auto state = KDBG::state();
+                                                       auto threadmap = KDBG::threadmap<SIZE>(state);
+                                                       auto cpumap = KDBG::cpumap();
+                                                        machine = std::make_unique<Machine<SIZE>>(cpumap.data(), (uint32_t)cpumap.size(),
+                                                                                                  threadmap.data(), (uint32_t)threadmap.size(),
+                                                                                                  events, count);
+
+                                                       if (globals.should_zero_base_timestamps() && count) {
+                                                               globals.set_beginning_of_time(events[0].timestamp());
+                                                       } else {
+                                                               globals.set_beginning_of_time(AbsTime(0));
+                                                       }
+                                               }
+
+                                               if (!machine->lost_events()) {
+                                                       process_events(globals, *machine, task_appnap_state, task_requested_state, thread_requested_state, task_effective_state, thread_effective_state, task_boosts);
+
+                                                       // We read to the end of the ring buffer, and there are
+                                                       // more events to process. Do not risk an overflow, process
+                                                       // them immediately.
+
+                                                       // If count == capacity, we read to the end of the ring buffer,
+                                                       // and should immediately re-read.
+                                                       if (count < capacity) {
+                                                               next_trace_update += traceUpdateIntervalAbs;
+                                                               if (next_trace_update <= now) {
+                                                                       printf("WARNING - falling behind on event processing\n");
+                                                                       // Reset so if we do catch up, we don't spin on a clock
+                                                                       // that has fallen seconds behind.
+                                                                       next_trace_update = AbsTime::now();
+                                                               }
+                                                       }
+                                               } else {
+                                                       printf("LOST EVENTS, exiting...\n");
+                                                       shouldProcessEvents = false;
+                                               }
+
+                                               last_machine = std::move(machine);
+                                       }
+                               }
+
+                               mach_wait_until(next_trace_update.value());
+                       }
+               } else {
+                       printf("Unable to enable tracing.\n");
+                       shouldProcessEvents = false;
+               }
+
+               signal(SIGINT, SIG_DFL);
+       }
+
+       // Final cleanup here to make sure partial initialization is
+       // cleaned up.
+       end_live_tracing();
+}
+
+void LiveTraceAction::execute(Globals& globals) {
+       // Initial state snapshot, is another program using the trace buffer, etc.
+       try {
+               KDState state = KDBG::state();
+               if (state.is_initialized() || state.controlling_pid() > 0) {
+                       if (state.controlling_pid() != getpid()) {
+                               if (state.controlling_pid() > 0 && kill(state.controlling_pid(), 0) == -1 && errno == ESRCH) {
+                                       if (globals.is_verbose()) {
+                                               printf("Reclaiming trace buffer control from pid %d\n", state.controlling_pid());
+                                       }
+                               } else {
+                                       printf("Another process is using the trace facility, possibly pid %d\n", state.controlling_pid());
+                                       exit(1);
+                               }
+                       }
+               }
+
+               try {
+                       if (state.is_lp64()) {
+                               live_trace_event_loop<Kernel64>(globals);
+                       } else {
+                               live_trace_event_loop<Kernel32>(globals);
+                       }
+               } catch (const std::exception& e) {
+                       log_msg(ASL_LEVEL_WARNING, "Caught exception in %s:\n %s\n", __PRETTY_FUNCTION__, e.what());
+                       KDBG::reset();
+               }
+
+       } catch (Exception& e) {
+               if (getuid() != 0) {
+                       printf("Unable to acquire trace buffer state. You must be root.\n");
+                       exit(1);
+               } else {
+                       usage(e.what());
+               }
+       }
+}