From 868ba6279a20e4d1412c2d576c67400167de6694 Mon Sep 17 00:00:00 2001 From: LaG1924 <12997935+LaG1924@users.noreply.github.com> Date: Tue, 30 Apr 2019 16:12:35 +0500 Subject: Integrated Optick profiler --- external/optick/optick_core.macos.h | 289 ++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 external/optick/optick_core.macos.h (limited to 'external/optick/optick_core.macos.h') diff --git a/external/optick/optick_core.macos.h b/external/optick/optick_core.macos.h new file mode 100644 index 0000000..3d19bfd --- /dev/null +++ b/external/optick/optick_core.macos.h @@ -0,0 +1,289 @@ +#pragma once +#if defined(__APPLE_CC__) + +#include "optick.config.h" +#if USE_OPTICK + +#include "optick_core.platform.h" + +#include +#include +#include +#include +#include + +namespace Optick +{ + const char* Platform::GetName() + { + return "MacOS"; + } + + ThreadID Platform::GetThreadID() + { + uint64_t tid; + pthread_threadid_np(pthread_self(), &tid); + return tid; + } + + ProcessID Platform::GetProcessID() + { + return (ProcessID)getpid(); + } + + int64 Platform::GetFrequency() + { + return 1000000000; + } + + int64 Platform::GetTime() + { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return ts.tv_sec * 1000000000LL + ts.tv_nsec; + } +} + +#if OPTICK_ENABLE_TRACING + +#include "optick_core.h" + +namespace Optick +{ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class DTrace : public Trace +{ + static const bool isSilent = true; + + std::thread processThread; + string password; + + enum State + { + STATE_IDLE, + STATE_RUNNING, + STATE_ABORT, + }; + + volatile State state; + volatile int64 timeout; + + struct CoreState + { + ProcessID pid; + ThreadID tid; + int prio; + bool IsValid() const { return tid != INVALID_THREAD_ID; } + CoreState() : pid(INVALID_PROCESS_ID), tid(INVALID_THREAD_ID), prio(0) {} + }; + static const int MAX_CPU_CORES = 256; + array cores; + + static void AsyncProcess(DTrace* trace); + void Process(); + + bool CheckRootAccess(); + + enum ParseResult + { + PARSE_OK, + PARSE_TIMEOUT, + PARSE_FAILED, + }; + ParseResult Parse(const char* line); +public: + + DTrace(); + + 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; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +DTrace g_DTrace; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +DTrace::DTrace() : state(STATE_IDLE), timeout(0) +{ +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool DTrace::CheckRootAccess() +{ + char cmd[256] = { 0 }; + sprintf_s(cmd, "echo \'%s\' | sudo -S echo %s", password.c_str(), isSilent ? "2> /dev/null" : ""); + return system(cmd) == 0; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +CaptureStatus::Type DTrace::Start(Mode::Type mode, int /*frequency*/, const ThreadList& /*threads*/) +{ + if (state == STATE_IDLE && (mode & Mode::SWITCH_CONTEXT) != 0) + { + if (!CheckRootAccess()) + return CaptureStatus::ERR_TRACER_INVALID_PASSWORD; + + state = STATE_RUNNING; + timeout = INT64_MAX; + cores.fill(CoreState()); + processThread = std::thread(AsyncProcess, this); + } + + return CaptureStatus::OK; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool DTrace::Stop() +{ + if (state != STATE_RUNNING) + { + return false; + } + + timeout = Platform::GetTime(); + processThread.join(); + state = STATE_IDLE; + + return true; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +FILE* popen2(const char *program, const char *type, pid_t* outPid) +{ + FILE *iop; + int pdes[2]; + pid_t pid; + if ((*type != 'r' && *type != 'w') || type[1] != '\0') { + errno = EINVAL; + return (NULL); + } + + if (pipe(pdes) < 0) { + return (NULL); + } + + switch (pid = fork()) { + case -1: /* Error. */ + (void)close(pdes[0]); + (void)close(pdes[1]); + return (NULL); + /* NOTREACHED */ + case 0: /* Child. */ + { + if (*type == 'r') { + (void)close(pdes[0]); + if (pdes[1] != STDOUT_FILENO) { + (void)dup2(pdes[1], STDOUT_FILENO); + (void)close(pdes[1]); + } + } + else { + (void)close(pdes[1]); + if (pdes[0] != STDIN_FILENO) { + (void)dup2(pdes[0], STDIN_FILENO); + (void)close(pdes[0]); + } + } + execl("/bin/sh", "sh", "-c", program, NULL); + perror("execl"); + exit(1); + /* NOTREACHED */ + } + } + /* Parent; assume fdopen can't fail. */ + if (*type == 'r') { + iop = fdopen(pdes[0], type); + (void)close(pdes[1]); + } + else { + iop = fdopen(pdes[1], type); + (void)close(pdes[0]); + } + + if (outPid) + *outPid = pid; + + return (iop); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void DTrace::Process() +{ + const char* command = "dtrace -n fbt::thread_dispatch:return'\\''{printf(\"@%d %d %d %d\", pid, tid, curthread->sched_pri, walltimestamp)}'\\''"; + + char buffer[256] = { 0 }; + sprintf_s(buffer, "echo \'%s\' | sudo -S sh -c \'%s\' %s", password.c_str(), command, isSilent ? "2> /dev/null" : ""); + pid_t pid; + if (FILE* pipe = popen2(buffer, "r", &pid)) + { + char* line = NULL; + size_t len = 0; + while (state == STATE_RUNNING && (getline(&line, &len, pipe)) != -1) + { + if (Parse(line) == PARSE_TIMEOUT) + break; + } + fclose(pipe); + + int internal_stat; + waitpid(pid, &internal_stat, 0); + } + else + { + OPTICK_FAILED("Failed to open communication pipe!"); + } +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +DTrace::ParseResult DTrace::Parse(const char* line) +{ + if (const char* cmd = strchr(line, '@')) + { + int cpu = atoi(line); + + CoreState currState; + + currState.pid = atoi(cmd + 1); + cmd = strchr(cmd, ' ') + 1; + + currState.tid = atoi(cmd); + cmd = strchr(cmd, ' ') + 1; + + currState.prio = atoi(cmd); + cmd = strchr(cmd, ' ') + 1; + + int64_t timestamp = (int64_t)atoll(cmd); + + if (timestamp > timeout) + return PARSE_TIMEOUT; + + const CoreState& prevState = cores[cpu]; + + if (prevState.IsValid()) + { + SwitchContextDesc desc; + desc.reason = 0; + desc.cpuId = cpu; + desc.oldThreadId = prevState.tid; + desc.newThreadId = currState.tid; + desc.timestamp = timestamp; + Core::Get().ReportSwitchContext(desc); + } + + cores[cpu] = currState; + } + return PARSE_FAILED; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void DTrace::AsyncProcess(DTrace *trace) { + trace->Process(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Trace* Platform::GetTrace() +{ + return &g_DTrace; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SymbolEngine* Platform::GetSymbolEngine() +{ + return nullptr; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} +#endif //OPTICK_ENABLE_TRACING +#endif //USE_OPTICK +#endif //__APPLE_CC__ \ No newline at end of file -- cgit v1.2.3