X-Git-Url: https://git.saurik.com/apple/system_cmds.git/blobdiff_plain/1a7e3f61d38d679bba59130891c2031b5a0092b6..bd6521f0fc816ab056bc71376f9706a69b3b52c1:/kdprof/SaveTraceAction.cpp diff --git a/kdprof/SaveTraceAction.cpp b/kdprof/SaveTraceAction.cpp new file mode 100644 index 0000000..ce95b37 --- /dev/null +++ b/kdprof/SaveTraceAction.cpp @@ -0,0 +1,101 @@ +// +// SaveTraceAction.cpp +// kdprof +// +// Created by James McIlree on 5/2/13. +// Copyright (c) 2013 Apple. All rights reserved. +// + +#include "global.h" + +template +static void execute_arch_specific(Globals& globals, KDState& state, FileDescriptor& save_fd) { + // Collect all data first, printing takes time... + auto threadmap = KDBG::threadmap(state); + auto cpumap = KDBG::cpumap(); + + // These are future proofing, trace doesn't actually need page alignment + // here, just file block size alignment. When page sizes go to 16k, we + // don't want 16k of padding. + +#define FILE_BLOCK_SIZE 4096 +#define FILE_BLOCK_SIZE_MASK 4095 + + /* + * To write a RAW_VERSION1+ file, we must embed a cpumap in the "padding" + * used to file block align the events folloing the threadmap. If the + * threadmap happens to not require enough padding, we artificially + * increase its footprint until it needs enough padding. + */ + + uint32_t pad_size = FILE_BLOCK_SIZE - ((sizeof(TraceDataHeader) + (threadmap.size() * sizeof(KDThreadMapEntry))) & FILE_BLOCK_SIZE_MASK); + uint32_t cpumap_size = sizeof(kd_cpumap_header) + (uint32_t)cpumap.size() * sizeof(KDCPUMapEntry); + uint32_t extra_thread_count = 0; + + if (cpumap_size > pad_size) { + /* Force an overflow onto the next page, we get a full page of padding */ + extra_thread_count = (pad_size / sizeof(KDCPUMapEntry)) + 1; + } + + // Write the header + TraceDataHeader header(RAW_VERSION1, (uint32_t)threadmap.size(), time(NULL), 0); + write(save_fd, &header, sizeof(TraceDataHeader)); + + // Write the threadmaps + write(save_fd, threadmap.data(), threadmap.size() * sizeof(KDThreadMapEntry)); + + if (extra_thread_count) { + pad_size = extra_thread_count * sizeof(KDThreadMapEntry); + auto pad_buf = (uint8_t *)calloc(pad_size, 1); + write(save_fd, pad_buf, pad_size); + free(pad_buf); + } + + // Write the cpumaps & any remaining padding + size_t bytes_written = sizeof(TraceDataHeader) + (threadmap.size() + extra_thread_count) * sizeof(KDThreadMapEntry); + pad_size = FILE_BLOCK_SIZE - (bytes_written & FILE_BLOCK_SIZE_MASK); + + ASSERT(pad_size >= cpumap.size() * sizeof(KDCPUMapEntry), "Not enough padding bytes!"); + if (pad_size) { + auto cpumap_header = (kd_cpumap_header*)calloc(pad_size, 1); + cpumap_header->version_no = RAW_VERSION1; + cpumap_header->cpu_count = (uint32_t)cpumap.size(); + auto cpus = (kd_cpumap*)&cpumap_header[1]; + memcpy(cpus, cpumap.data(), cpumap.size() * sizeof(KDCPUMapEntry)); + write(save_fd, cpumap_header, pad_size); + } + + // Write the events + // + // Because this may be used to capture boot traces which consume very + // large amounts of memory, we will likely not be able to collect + // the entire buffer space in a single shot. Read it in small chunks. + // + auto twenty_mb = 20 * 1024 * 1024; + auto num_events_in_twenty_mb = twenty_mb / sizeof(KDEvent); + MemoryBuffer> events(num_events_in_twenty_mb); + + // We read until we don't get back a full buffer, hoping thats enough. + while (1) { + int count = KDBG::read(events.data(), events.capacity_in_bytes()); + + if (count != -1) + write(save_fd, events.data(), count * sizeof(KDEvent)); + + if (count < num_events_in_twenty_mb) { + break; + } + } + + // close up + save_fd.close(); +} + +void SaveTraceAction::execute(Globals& globals) { + KDState state = KDBG::state(); + if (state.is_lp64()) { + execute_arch_specific(globals, state, _save_fd); + } else { + execute_arch_specific(globals, state, _save_fd); + } +}