+++ /dev/null
-//
-// 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());
- }
- }
-}