Implement Perfetto Tracing

This commit implements tracing Skyline using https://perfetto.dev/ for SVCs, IPC, Scheduler and Presentation
This commit is contained in:
Billy Laws 2021-03-12 00:11:12 +05:30 committed by ◱ Mark
parent 1155394d58
commit 6c6e665569
14 changed files with 74 additions and 3 deletions

4
.gitmodules vendored
View File

@ -21,3 +21,7 @@
path = app/libraries/tzcode path = app/libraries/tzcode
url = https://github.com/skyline-emu/tz url = https://github.com/skyline-emu/tz
branch = master branch = master
[submodule "app/libraries/perfetto"]
path = app/libraries/perfetto
url = https://android.googlesource.com/platform/external/perfetto
branch = releases/v12.x

View File

@ -32,6 +32,10 @@ include_directories("libraries/frozen/include")
find_package(mbedtls REQUIRED CONFIG) find_package(mbedtls REQUIRED CONFIG)
# Define a static library for Perfetto.
include_directories(libraries/perfetto/sdk)
add_library(perfetto STATIC libraries/perfetto/sdk/perfetto.cc)
include_directories(${source_DIR}/skyline) include_directories(${source_DIR}/skyline)
add_library(skyline SHARED add_library(skyline SHARED
@ -41,6 +45,7 @@ add_library(skyline SHARED
${source_DIR}/skyline/common/settings.cpp ${source_DIR}/skyline/common/settings.cpp
${source_DIR}/skyline/common/signal.cpp ${source_DIR}/skyline/common/signal.cpp
${source_DIR}/skyline/common/uuid.cpp ${source_DIR}/skyline/common/uuid.cpp
${source_DIR}/skyline/common/tracing.cpp
${source_DIR}/skyline/nce/guest.S ${source_DIR}/skyline/nce/guest.S
${source_DIR}/skyline/nce.cpp ${source_DIR}/skyline/nce.cpp
${source_DIR}/skyline/jvm.cpp ${source_DIR}/skyline/jvm.cpp
@ -180,5 +185,5 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/prepo/IPrepoService.cpp ${source_DIR}/skyline/services/prepo/IPrepoService.cpp
) )
# target_precompile_headers(skyline PRIVATE ${source_DIR}/skyline/common.h) # PCH will currently break Intellisense # target_precompile_headers(skyline PRIVATE ${source_DIR}/skyline/common.h) # PCH will currently break Intellisense
target_link_libraries(skyline vulkan android fmt lz4_static tzcode oboe mbedtls::mbedcrypto) target_link_libraries(skyline vulkan android perfetto fmt lz4_static tzcode oboe mbedtls::mbedcrypto)
target_compile_options(skyline PRIVATE -Wall -Wno-unknown-attributes -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c99-designator -Wno-reorder -Wno-missing-braces -Wno-unused-variable -Wno-unused-private-field) target_compile_options(skyline PRIVATE -Wall -Wno-unknown-attributes -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c99-designator -Wno-reorder -Wno-missing-braces -Wno-unused-variable -Wno-unused-private-field)

@ -0,0 +1 @@
Subproject commit 3f02be823cef0f54e720c0382ffc4507f48e6e4b

View File

@ -10,6 +10,7 @@
#include "skyline/common.h" #include "skyline/common.h"
#include "skyline/common/signal.h" #include "skyline/common/signal.h"
#include "skyline/common/settings.h" #include "skyline/common/settings.h"
#include "skyline/common/tracing.h"
#include "skyline/loader/loader.h" #include "skyline/loader/loader.h"
#include "skyline/vfs/android_asset_filesystem.h" #include "skyline/vfs/android_asset_filesystem.h"
#include "skyline/os.h" #include "skyline/os.h"
@ -64,6 +65,12 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
auto start{std::chrono::steady_clock::now()}; auto start{std::chrono::steady_clock::now()};
// Initialize tracing
perfetto::TracingInitArgs args;
args.backends |= perfetto::kSystemBackend;
perfetto::Tracing::Initialize(args);
perfetto::TrackEvent::Register();
try { try {
auto os{std::make_shared<skyline::kernel::OS>(jvmManager, logger, settings, std::string(appFilesPath), GetTimeZoneName(), std::make_shared<skyline::vfs::AndroidAssetFileSystem>(AAssetManager_fromJava(env, assetManager)))}; auto os{std::make_shared<skyline::kernel::OS>(jvmManager, logger, settings, std::string(appFilesPath), GetTimeZoneName(), std::make_shared<skyline::vfs::AndroidAssetFileSystem>(AAssetManager_fromJava(env, assetManager)))};
OsWeak = os; OsWeak = os;
@ -85,6 +92,8 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
logger->Error("An unknown exception has occurred"); logger->Error("An unknown exception has occurred");
} }
perfetto::TrackEvent::Flush();
InputWeak.reset(); InputWeak.reset();
logger->Info("Emulation has ended"); logger->Info("Emulation has ended");

View File

@ -0,0 +1,3 @@
#include "tracing.h"
PERFETTO_TRACK_EVENT_STATIC_STORAGE();

View File

@ -0,0 +1,19 @@
#pragma once
#include <limits>
#include <perfetto.h>
#include <common.h>
PERFETTO_DEFINE_CATEGORIES(perfetto::Category("sched").SetDescription("Events from the scheduler"),
perfetto::Category("kernel").SetDescription("Events from parts of the kernel"),
perfetto::Category("guest").SetDescription("Events relating to guest code"),
perfetto::Category("gpu").SetDescription("Events from the emulated GPU"));
namespace skyline::tracing {
/**
* @brief Perfetto track IDs for custom tracks, counting down from U64 max to avoid conflicts
*/
enum class TrackIds : u64 {
Presentation = std::numeric_limits<u64>::max()
};
}

View File

@ -9,7 +9,11 @@ extern skyline::u16 Fps;
extern skyline::u32 FrameTime; extern skyline::u32 FrameTime;
namespace skyline::gpu { namespace skyline::gpu {
PresentationEngine::PresentationEngine(const DeviceState &state) : state(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state, true)), bufferEvent(std::make_shared<kernel::type::KEvent>(state, true)) {} PresentationEngine::PresentationEngine(const DeviceState &state) : state(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state, true)), bufferEvent(std::make_shared<kernel::type::KEvent>(state, true)), presentationTrack(static_cast<uint64_t>(tracing::TrackIds::Presentation), perfetto::ProcessTrack::Current()) {
auto desc{presentationTrack.Serialize()};
desc.set_name("Presentation");
perfetto::TrackEvent::SetTrackDescriptor(presentationTrack, desc);
}
PresentationEngine::~PresentationEngine() { PresentationEngine::~PresentationEngine() {
if (window) if (window)
@ -76,6 +80,8 @@ namespace skyline::gpu {
FrameTime = static_cast<u32>((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals FrameTime = static_cast<u32>((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals
Fps = static_cast<u16>(constant::NsInSecond / (now - frameTimestamp)); Fps = static_cast<u16>(constant::NsInSecond / (now - frameTimestamp));
TRACE_EVENT_INSTANT("gpu", "Present", presentationTrack, "FrameTimeNs", now - frameTimestamp, "Fps", Fps);
frameTimestamp = now; frameTimestamp = now;
} else { } else {
frameTimestamp = util::GetTimeNs(); frameTimestamp = util::GetTimeNs();

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include <common/tracing.h>
#include <kernel/types/KEvent.h> #include <kernel/types/KEvent.h>
#include "texture.h" #include "texture.h"
@ -16,6 +17,7 @@ namespace skyline::gpu {
std::condition_variable windowConditional; std::condition_variable windowConditional;
jobject surface{}; //!< The Surface object backing the ANativeWindow jobject surface{}; //!< The Surface object backing the ANativeWindow
u64 frameTimestamp{}; //!< The timestamp of the last frame being shown u64 frameTimestamp{}; //!< The timestamp of the last frame being shown
perfetto::Track presentationTrack; //!< Perfetto track used for presentation events
public: public:
texture::Dimensions resolution{}; texture::Dimensions resolution{};

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <common/tracing.h>
#include <android/native_window.h> #include <android/native_window.h>
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <unistd.h> #include <unistd.h>
@ -22,6 +23,7 @@ namespace skyline::gpu {
} }
void Texture::SynchronizeHost() { void Texture::SynchronizeHost() {
TRACE_EVENT("gpu", "Texture::SynchronizeHost");
auto pointer{guest->pointer}; auto pointer{guest->pointer};
auto size{format.GetSize(dimensions)}; auto size{format.GetSize(dimensions)};
backing.resize(size); backing.resize(size);

View File

@ -3,6 +3,7 @@
#include <unistd.h> #include <unistd.h>
#include <common/signal.h> #include <common/signal.h>
#include <common/tracing.h>
#include "types/KThread.h" #include "types/KThread.h"
#include "scheduler.h" #include "scheduler.h"
@ -12,6 +13,8 @@ namespace skyline::kernel {
Scheduler::Scheduler(const DeviceState &state) : state(state) {} Scheduler::Scheduler(const DeviceState &state) : state(state) {}
void Scheduler::SignalHandler(int signal, siginfo *info, ucontext *ctx, void **tls) { void Scheduler::SignalHandler(int signal, siginfo *info, ucontext *ctx, void **tls) {
TRACE_EVENT_END("guest");
if (*tls) { if (*tls) {
const auto &state{*reinterpret_cast<nce::ThreadContext *>(*tls)->state}; const auto &state{*reinterpret_cast<nce::ThreadContext *>(*tls)->state};
if (signal == PreemptionSignal) if (signal == PreemptionSignal)
@ -22,6 +25,7 @@ namespace skyline::kernel {
} else { } else {
YieldPending = true; YieldPending = true;
} }
TRACE_EVENT_BEGIN("guest", "Guest");
} }
Scheduler::CoreContext &Scheduler::GetOptimalCoreForThread(const std::shared_ptr<type::KThread> &thread) { Scheduler::CoreContext &Scheduler::GetOptimalCoreForThread(const std::shared_ptr<type::KThread> &thread) {
@ -150,6 +154,7 @@ namespace skyline::kernel {
return !core->queue.empty() && core->queue.front() == thread; return !core->queue.empty() && core->queue.front() == thread;
}}; }};
TRACE_EVENT("sched", "WaitSchedule");
if (loadBalance && thread->affinityMask.count() > 1) { if (loadBalance && thread->affinityMask.count() > 1) {
std::chrono::milliseconds loadBalanceThreshold{PreemptiveTimeslice * 2}; //!< The amount of time that needs to pass unscheduled for a thread to attempt load balancing std::chrono::milliseconds loadBalanceThreshold{PreemptiveTimeslice * 2}; //!< The amount of time that needs to pass unscheduled for a thread to attempt load balancing
while (!thread->scheduleCondition.wait_for(lock, loadBalanceThreshold, wakeFunction)) { while (!thread->scheduleCondition.wait_for(lock, loadBalanceThreshold, wakeFunction)) {
@ -177,6 +182,7 @@ namespace skyline::kernel {
auto &thread{state.thread}; auto &thread{state.thread};
auto *core{&cores.at(thread->coreId)}; auto *core{&cores.at(thread->coreId)};
TRACE_EVENT("sched", "TimedWaitSchedule");
std::unique_lock lock(core->mutex); std::unique_lock lock(core->mutex);
if (thread->scheduleCondition.wait_for(lock, timeout, [&]() { if (thread->scheduleCondition.wait_for(lock, timeout, [&]() {
if (!thread->affinityMask.test(thread->coreId)) [[unlikely]] { if (!thread->affinityMask.test(thread->coreId)) [[unlikely]] {
@ -201,6 +207,7 @@ namespace skyline::kernel {
auto &core{cores.at(thread->coreId)}; auto &core{cores.at(thread->coreId)};
std::unique_lock lock(core.mutex); std::unique_lock lock(core.mutex);
if (core.queue.front() == thread) { if (core.queue.front() == thread) {
// If this thread is at the front of the thread queue then we need to rotate the thread // If this thread is at the front of the thread queue then we need to rotate the thread
// In the case where this thread was forcefully yielded, we don't need to do this as it's done by the thread which yielded to this thread // In the case where this thread was forcefully yielded, we don't need to do this as it's done by the thread which yielded to this thread

View File

@ -3,6 +3,7 @@
#include <os.h> #include <os.h>
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <common/tracing.h>
#include <vfs/npdm.h> #include <vfs/npdm.h>
#include "results.h" #include "results.h"
#include "svc.h" #include "svc.h"
@ -286,6 +287,8 @@ namespace skyline::kernel::svc {
constexpr i64 yieldWithCoreMigration{-1}; constexpr i64 yieldWithCoreMigration{-1};
constexpr i64 yieldToAnyThread{-2}; constexpr i64 yieldToAnyThread{-2};
TRACE_EVENT("kernel", "SleepThread");
i64 in{static_cast<i64>(state.ctx->gpr.x0)}; i64 in{static_cast<i64>(state.ctx->gpr.x0)};
if (in > 0) { if (in > 0) {
state.logger->Debug("svcSleepThread: Sleeping for {}ns", in); state.logger->Debug("svcSleepThread: Sleeping for {}ns", in);

View File

@ -4,6 +4,7 @@
#include <cxxabi.h> #include <cxxabi.h>
#include <unistd.h> #include <unistd.h>
#include <common/signal.h> #include <common/signal.h>
#include <common/tracing.h>
#include <nce.h> #include <nce.h>
#include <os.h> #include <os.h>
#include "KProcess.h" #include "KProcess.h"
@ -82,6 +83,8 @@ namespace skyline::kernel::type {
state.scheduler->WaitSchedule(); state.scheduler->WaitSchedule();
} }
TRACE_EVENT_BEGIN("guest", "Guest");
asm volatile( asm volatile(
"MRS X0, TPIDR_EL0\n\t" "MRS X0, TPIDR_EL0\n\t"
"MSR TPIDR_EL0, %x0\n\t" // Set TLS to ThreadContext "MSR TPIDR_EL0, %x0\n\t" // Set TLS to ThreadContext

View File

@ -4,8 +4,8 @@
#include <cxxabi.h> #include <cxxabi.h>
#include <unistd.h> #include <unistd.h>
#include "common/signal.h" #include "common/signal.h"
#include "common/tracing.h"
#include "os.h" #include "os.h"
#include "gpu.h"
#include "jvm.h" #include "jvm.h"
#include "kernel/types/KProcess.h" #include "kernel/types/KProcess.h"
#include "kernel/svc.h" #include "kernel/svc.h"
@ -15,10 +15,13 @@
namespace skyline::nce { namespace skyline::nce {
void NCE::SvcHandler(u16 svc, ThreadContext *ctx) { void NCE::SvcHandler(u16 svc, ThreadContext *ctx) {
TRACE_EVENT_END("guest");
const auto &state{*ctx->state}; const auto &state{*ctx->state};
try { try {
auto function{kernel::svc::SvcTable[svc]}; auto function{kernel::svc::SvcTable[svc]};
if (function) [[likely]] { if (function) [[likely]] {
TRACE_EVENT("kernel", "SVC");
state.logger->Debug("SVC called 0x{:X}", svc); state.logger->Debug("SVC called 0x{:X}", svc);
(*function)(state); (*function)(state);
} else [[unlikely]] { } else [[unlikely]] {
@ -49,6 +52,8 @@ namespace skyline::nce {
abi::__cxa_end_catch(); abi::__cxa_end_catch();
std::longjmp(state.thread->originalCtx, true); std::longjmp(state.thread->originalCtx, true);
} }
TRACE_EVENT_BEGIN("guest", "Guest");
} }
void NCE::SignalHandler(int signal, siginfo *info, ucontext *ctx, void **tls) { void NCE::SignalHandler(int signal, siginfo *info, ucontext *ctx, void **tls) {

View File

@ -2,6 +2,7 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <common/tracing.h>
#include "sm/IUserInterface.h" #include "sm/IUserInterface.h"
#include "settings/ISettingsServer.h" #include "settings/ISettingsServer.h"
#include "settings/ISystemSettingsServer.h" #include "settings/ISystemSettingsServer.h"
@ -146,6 +147,7 @@ namespace skyline::service {
} }
void ServiceManager::SyncRequestHandler(KHandle handle) { void ServiceManager::SyncRequestHandler(KHandle handle) {
TRACE_EVENT("kernel", "ServiceManager::SyncRequestHandler");
auto session{state.process->GetHandle<type::KSession>(handle)}; auto session{state.process->GetHandle<type::KSession>(handle)};
state.logger->Verbose("----IPC Start----"); state.logger->Verbose("----IPC Start----");
state.logger->Verbose("Handle is 0x{:X}", handle); state.logger->Verbose("Handle is 0x{:X}", handle);