#pragma once #if defined(__linux__) #include "optick.config.h" #if USE_OPTICK #include "optick_core.platform.h" #include #include #include #include #include namespace Optick { const char* Platform::GetName() { return "Linux"; } ThreadID Platform::GetThreadID() { return syscall(SYS_gettid); } ProcessID Platform::GetProcessID() { return (ProcessID)getpid(); } int64 Platform::GetFrequency() { return 1000000000; } int64 Platform::GetTime() { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000000000LL + ts.tv_nsec; } } #if OPTICK_ENABLE_TRACING #include "optick_memory.h" //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// namespace ft { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct base_event { int64_t timestamp; short common_type; uint8_t cpu_id; base_event(short type) : timestamp(-1), common_type(type), cpu_id(uint8_t(-1)) {} }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template struct event : public base_event { static const short type = TYPE; event() : base_event(TYPE) {} }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct process_state { enum type { Unknown, //D Uninterruptible sleep(usually IO) UninterruptibleSleep, //R Running or runnable(on run queue) Running, //S Interruptible sleep(waiting for an event to complete) InterruptibleSleep, //T Stopped, either by a job control signal or because it is being traced. Stopped, //X dead(should never be seen) Dead, //Z Defunct(“zombie”) process, terminated but not reaped by its parent. Zombie, }; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct sched_switch : public event<305> { char prev_comm[16]; pid_t prev_pid; int prev_prio; process_state::type prev_state; char next_comm[16]; pid_t next_pid; int next_prio; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace ft //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// namespace Optick { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static const char* KERNEL_TRACING_PATH = "/sys/kernel/debug/tracing"; static const char* FTRACE_TRACE = "trace"; static const char* FTRACE_TRACING_ON = "tracing_on"; static const char* FTRACE_TRACE_CLOCK = "trace_clock"; static const char* FTRACE_OPTIONS_IRQ_INFO = "options/irq-info"; static const char* FTRACE_SCHED_SWITCH = "events/sched/sched_switch/enable"; static const uint8_t PROCESS_STATE_REASON_START = 38; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class FTrace : public Trace { bool isActive; string password; bool Parse(const char* line); bool ProcessEvent(const ft::base_event& ev); void Set(const char* name, bool value); void Set(const char* name, const char* value); void Exec(const char* cmd); public: FTrace(); ~FTrace(); virtual void SetPassword(const char* pwd) override { password = pwd; } virtual CaptureStatus::Type Start(Mode::Type mode, int frequency, const ThreadList& threads) override; virtual bool Stop() override; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FTrace g_FTrace; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct Parser { const char* cursor; const char* finish; size_t length; Parser(const char* b) : cursor(b), finish(b + strlen(b)) {} bool Skip(size_t count) { if ((size_t)(finish - cursor) > count) { cursor += count; return true; } return false; } bool Skip(const char* text, char* output = nullptr, size_t size = 0) { if (const char* ptr = strstr(cursor, text)) { if (output != nullptr) { size_t count = std::min(size - 1, (size_t)(ptr - cursor)); strncpy(output, cursor, count); output[count] = '\0'; } cursor = ptr + strlen(text); return true; } return false; } void SkipSpaces() { while (cursor != finish && (*cursor == ' ' || *cursor == '\t' || *cursor == '\n')) ++cursor; } bool Starts(const char* text) const { return strncmp(cursor, text, strlen(text)) == 0; } int GetInt() const { return atoi(cursor); } char GetChar() const { return *cursor; } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// CaptureStatus::Type FTrace::Start(Mode::Type mode, int /*frequency*/, const ThreadList& /*threads*/) { if (!isActive) { // Disable tracing Set(FTRACE_TRACING_ON, false); // Cleanup old data Set(FTRACE_TRACE, ""); // Set clock type Set(FTRACE_TRACE_CLOCK, "mono"); // Disable irq info Set(FTRACE_OPTIONS_IRQ_INFO, false); // Enable switch events Set(FTRACE_SCHED_SWITCH, (mode & Mode::SWITCH_CONTEXT) != 0); // Enable tracing Set(FTRACE_TRACING_ON, true); isActive = true; } return CaptureStatus::OK; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool FTrace::Stop() { if (!isActive) { return false; } // Reset variables Set(FTRACE_TRACING_ON, false); Set(FTRACE_SCHED_SWITCH, false); // Parsing the output char buffer[256] = { 0 }; sprintf_s(buffer, "echo \'%s\' | sudo -S sh -c \'cat %s/%s\'", password.c_str(), KERNEL_TRACING_PATH, FTRACE_TRACE); if (FILE* pipe = popen(buffer, "r")) { char* line = NULL; size_t len = 0; while ((getline(&line, &len, pipe)) != -1) Parse(line); fclose(pipe); } // Cleanup data Set(FTRACE_TRACE, ""); isActive = false; return true; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool FTrace::Parse(const char * line) { // sched_switch: // ConsoleApp-8687 [000] 181944.352057: sched_switch: prev_comm=ConsoleApp prev_pid=8687 prev_prio=120 prev_state=S ==> next_comm=ConsoleApp next_pid=8686 next_prio=120 Parser p(line); if (p.Starts("#")) return true; if (!p.Skip(16)) return false; if (!p.Skip("[")) return false; int cpu = p.GetInt(); if (!p.Skip("]")) return false; int64 timestampInt = p.GetInt(); if (!p.Skip(".")) return false; int64 timestampFraq = p.GetInt(); if (!p.Skip(": ")) return false; int64 timestamp = ((timestampInt * 1000000) + timestampFraq) * 1000; if (p.Starts("sched_switch:")) { ft::sched_switch ev; ev.cpu_id = cpu; ev.timestamp = timestamp; if (!p.Skip("prev_comm=")) return false; if (!p.Skip(" prev_pid=", ev.prev_comm, OPTICK_ARRAY_SIZE(ev.prev_comm))) return false; ev.prev_pid = p.GetInt(); if (!p.Skip(" prev_prio=")) return false; ev.prev_prio = p.GetInt(); if (!p.Skip(" prev_state=")) return false; switch (p.GetChar()) { case 'D': ev.prev_state = ft::process_state::UninterruptibleSleep; break; case 'R': ev.prev_state = ft::process_state::Running; break; case 'S': ev.prev_state = ft::process_state::InterruptibleSleep; break; case 'T': ev.prev_state = ft::process_state::Stopped; break; case 'X': ev.prev_state = ft::process_state::Dead; break; case 'Z': ev.prev_state = ft::process_state::Zombie; break; default: ev.prev_state = ft::process_state::Unknown; break; } if (!p.Skip("==> next_comm=")) return false; if (!p.Skip(" next_pid=", ev.next_comm, OPTICK_ARRAY_SIZE(ev.prev_comm))) return false; ev.next_pid = p.GetInt(); if (!p.Skip(" next_prio=")) return false; ev.next_prio = p.GetInt(); return ProcessEvent(ev); } return true; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool FTrace::ProcessEvent(const ft::base_event& ev) { switch (ev.common_type) { case ft::sched_switch::type: { const ft::sched_switch& switchEv = (const ft::sched_switch&)ev; SwitchContextDesc desc; desc.reason = switchEv.prev_state + PROCESS_STATE_REASON_START; desc.cpuId = switchEv.cpu_id; desc.oldThreadId = (uint64)switchEv.prev_pid; desc.newThreadId = (uint64)switchEv.next_pid; desc.timestamp = switchEv.timestamp; Core::Get().ReportSwitchContext(desc); return true; } break; } return false; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FTrace::Set(const char * name, bool value) { Set(name, value ? "1" : "0"); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FTrace::Set(const char* name, const char* value) { char buffer[256] = { 0 }; sprintf_s(buffer, "echo %s > %s/%s", value, KERNEL_TRACING_PATH, name); Exec(buffer); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FTrace::Exec(const char* cmd) { char buffer[256] = { 0 }; sprintf_s(buffer, "echo \'%s\' | sudo -S sh -c \'%s\'", password.c_str(), cmd); std::system(buffer); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FTrace::FTrace() : isActive(false) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FTrace::~FTrace() { Stop(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Trace* Platform::GetTrace() { return &g_FTrace; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// SymbolEngine* Platform::GetSymbolEngine() { return nullptr; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// } #endif //OPTICK_ENABLE_TRACING #endif //USE_OPTICK #endif //__linux__