summaryrefslogtreecommitdiffstats
path: root/external/optick/optick_core.macos.h
diff options
context:
space:
mode:
Diffstat (limited to 'external/optick/optick_core.macos.h')
-rw-r--r--external/optick/optick_core.macos.h289
1 files changed, 289 insertions, 0 deletions
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 <mach/mach_time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <unistd.h>
+
+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<CoreState, MAX_CPU_CORES> 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