Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions core/libs/prx/libSceAgcDriver/Execution/src/WorkerSampler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
#include <array>
#include <atomic>
#include <chrono>
#include <filesystem>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <stop_token>
#include <system_error>
Expand All @@ -30,6 +32,20 @@ std::jthread processSamplerThread;
std::jthread workerSamplerThread;
std::jthread committerSamplerThread;

std::optional<std::string> ConfiguredPath(const wchar_t* value) {
if (value == nullptr) return std::nullopt;
const auto text = std::filesystem::path(value).u8string();
return std::string(reinterpret_cast<const char*>(text.data()), text.size());
}

std::filesystem::path NativePath(const std::string& path) {
return std::filesystem::path(reinterpret_cast<const char8_t*>(path.c_str()));
}

std::FILE* OpenForWriting(const std::string& path) {
return _wfopen(NativePath(path).c_str(), L"w");
}

// Debug aid: APS5_SAMPLE_WORKER=<file> samples the calling thread every millisecond and, every 20 s,
// writes "module+offset self inclusive" lines to <file> for offline symbolization with nm.
struct Sampler {
Expand Down Expand Up @@ -173,7 +189,7 @@ struct Sampler {

void write() {
std::lock_guard lock(mutex);
std::FILE* file = std::fopen(path.c_str(), "w");
std::FILE* file = OpenForWriting(path);
if (file == nullptr) return;
std::fprintf(file, "# %llu samples\n", static_cast<unsigned long long>(samples));
std::map<std::uint64_t, std::string> loaded;
Expand All @@ -199,7 +215,7 @@ struct Sampler {
std::map<std::uint64_t, std::string> names;
std::map<std::uint64_t, std::string> loaded;
for (const auto& [address, count] : counts) names.emplace(address, frameName(address, loaded));
std::FILE* file = std::fopen(temporary.c_str(), "w");
std::FILE* file = OpenForWriting(temporary);
if (file == nullptr) return;
LARGE_INTEGER frequency{};
QueryPerformanceFrequency(&frequency);
Expand All @@ -210,7 +226,7 @@ struct Sampler {
const auto text = stacks.Format([&](std::uint64_t address) { return names.at(address); });
const bool written = std::fwrite(text.data(), 1, text.size(), file) == text.size();
if (std::fclose(file) != 0 || !written) return;
MoveFileExA(temporary.c_str(), collapsed.c_str(), MOVEFILE_REPLACE_EXISTING);
MoveFileExW(NativePath(temporary).c_str(), NativePath(collapsed).c_str(), MOVEFILE_REPLACE_EXISTING);
}
};

Expand Down Expand Up @@ -250,7 +266,7 @@ struct ProcessSampler {
}

void write() {
std::FILE* file = std::fopen(path.c_str(), "w");
std::FILE* file = OpenForWriting(path);
if (file == nullptr) return;
std::fprintf(file, "# %llu rounds\n", static_cast<unsigned long long>(rounds));
std::map<std::uint64_t, std::string> loaded;
Expand Down Expand Up @@ -278,9 +294,9 @@ struct ProcessSampler {
};

void StartProcessSampler() {
const char* path = std::getenv("APS5_SAMPLE_THREADS");
if (path == nullptr) return;
processSamplerThread = std::jthread([path = std::string(path)](std::stop_token token) {
const auto path = ConfiguredPath(_wgetenv(L"APS5_SAMPLE_THREADS"));
if (!path) return;
processSamplerThread = std::jthread([path = *path](std::stop_token token) {
auto sampler = std::make_unique<ProcessSampler>();
sampler->path = path;
sampler->self = GetCurrentThreadId();
Expand All @@ -297,10 +313,10 @@ void StartProcessSampler() {
});
}

void StartCallingThreadSampler(const char* path, std::jthread& slot) {
if (path == nullptr) return;
void StartCallingThreadSampler(const std::optional<std::string>& path, std::jthread& slot) {
if (!path) return;
auto sampler = std::make_unique<Sampler>();
sampler->path = path;
sampler->path = *path;
sampler->depth = Sampler::WorkerDepth;
sampler->keepStacks = true;
const auto* block = reinterpret_cast<const NT_TIB*>(NtCurrentTeb());
Expand Down Expand Up @@ -332,11 +348,11 @@ void StartWorkerSampler(std::uint32_t queue) {
const char* value = std::getenv("APS5_SAMPLE_WORKER_QUEUE");
return value != nullptr ? static_cast<std::uint32_t>(std::strtoul(value, nullptr, 0)) : 0u;
}();
if (queue == sampledQueue) StartCallingThreadSampler(std::getenv("APS5_SAMPLE_WORKER"), workerSamplerThread);
if (queue == sampledQueue) StartCallingThreadSampler(ConfiguredPath(_wgetenv(L"APS5_SAMPLE_WORKER")), workerSamplerThread);
}

void StartCommitterSampler() {
StartCallingThreadSampler(std::getenv("APS5_SAMPLE_COMMITTER"), committerSamplerThread);
StartCallingThreadSampler(ConfiguredPath(_wgetenv(L"APS5_SAMPLE_COMMITTER")), committerSamplerThread);
}

void StopWorkerSampler() {
Expand Down
29 changes: 21 additions & 8 deletions core/libs/prx/libSceAgcDriver/tests/WorkerSampler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,39 @@ int main(int argc, char** argv) {
const bool process = mode == "process";
const bool workerExpected = selected || mode == "default";
const auto stem = std::filesystem::temp_directory_path() / ("anyps5-sampler-" + std::to_string(GetCurrentProcessId()));
const auto workerPath = stem.string() + "-worker.txt";
const auto processPath = stem.string() + "-process.txt";
_putenv_s("APS5_SAMPLE_WORKER", workerPath.c_str());
_putenv_s("APS5_SAMPLE_THREADS", process ? processPath.c_str() : "");
auto workerPath = stem;
workerPath += "-worker.txt";
auto processPath = stem;
processPath += "-process.txt";
_wputenv_s(L"APS5_SAMPLE_WORKER", workerPath.c_str());
_wputenv_s(L"APS5_SAMPLE_THREADS", process ? processPath.c_str() : L"");
_putenv_s("APS5_SAMPLE_WORKER_QUEUE", mode == "default" ? "" : "0x50");
AgcDriver::StartWorkerSampler(selected ? 0x50 : 0);
const auto until = std::chrono::steady_clock::now() + std::chrono::milliseconds(200);
while (std::chrono::steady_clock::now() < until) std::this_thread::yield();
AgcDriver::StopWorkerSampler();
const auto sampled = [](const std::string& path) {
const auto sampled = [](const std::filesystem::path& path) {
std::ifstream file(path);
std::string marker;
std::uint64_t samples = 0;
return (file >> marker >> samples) && marker == "#" && samples != 0;
};
const bool passed = sampled(workerPath) == workerExpected && sampled(processPath) == process;
auto collapsed = stem;
collapsed += "-worker.collapsed.txt";
auto temporary = collapsed;
temporary += ".tmp";
const auto published = [&] {
std::ifstream file(collapsed);
std::string header;
unsigned long long samples = 0;
return std::getline(file, header) && std::sscanf(header.c_str(), "# samples=%llu", &samples) == 1 && samples != 0 &&
!std::filesystem::exists(temporary);
};
const bool passed = sampled(workerPath) == workerExpected && sampled(processPath) == process && published() == workerExpected;
std::filesystem::remove(workerPath);
std::filesystem::remove(processPath);
std::filesystem::remove(stem.string() + "-worker.collapsed.txt");
std::filesystem::remove(stem.string() + "-worker.collapsed.txt.tmp");
std::filesystem::remove(collapsed);
std::filesystem::remove(temporary);
std::printf("worker sampler %s: %s\n", mode.c_str(), passed ? "passed" : "failed");
return passed ? 0 : 1;
}
17 changes: 15 additions & 2 deletions core/libs/prx/libSceVideoOut/src/Telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <condition_variable>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <stdexcept>
#include <utility>
Expand All @@ -29,6 +30,18 @@ std::int64_t steadyNanos() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
}

std::string configuredDirectory() {
#ifdef _WIN32
const wchar_t* configured = _wgetenv(L"APS5_VIDEOOUT_TELEMETRY_DIR");
if (configured == nullptr) return {};
const auto text = std::filesystem::path(configured).u8string();
return std::string(reinterpret_cast<const char*>(text.data()), text.size());
#else
const char* configured = std::getenv("APS5_VIDEOOUT_TELEMETRY_DIR");
return configured != nullptr ? configured : std::string();
#endif
}

std::int64_t processId() {
#ifdef _WIN32
return static_cast<std::int64_t>(::GetCurrentProcessId());
Expand Down Expand Up @@ -82,7 +95,7 @@ std::uint64_t BoundedLineQueue::Dropped() const {

Writer::Writer(const std::string& directory, std::size_t capacity, const std::string& commit) : queue(capacity) {
const std::string path = directory + "/videoout.jsonl";
output.open(path, std::ios::binary | std::ios::app);
output.open(std::filesystem::path(reinterpret_cast<const char8_t*>(path.c_str())), std::ios::binary | std::ios::app);
if (!output.is_open()) throw std::runtime_error("VideoOut telemetry: cannot open " + path);
output.exceptions(std::ios::badbit | std::ios::failbit);
const auto steady = steadyNanos();
Expand Down Expand Up @@ -188,7 +201,7 @@ std::unique_ptr<Writer> OpenFromEnvironment(const char* directory, const std::st
}

Writer* Instance() {
static const std::unique_ptr<Writer> writer = OpenFromEnvironment(std::getenv("APS5_VIDEOOUT_TELEMETRY_DIR"), APS5_TRACE_COMMIT);
static const std::unique_ptr<Writer> writer = OpenFromEnvironment(configuredDirectory().c_str(), APS5_TRACE_COMMIT);
return writer.get();
}

Expand Down
Loading