Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
10 changes: 10 additions & 0 deletions sycl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,16 @@ set( SYCL_TOOLCHAIN_DEPLOY_COMPONENTS
${XPTIFW_LIBS}
)

if (TARGET sycl-prof)
list(APPEND SYCL_TOOLCHAIN_DEPLOY_COMPONENTS sycl-prof)
endif()
if (TARGET sycl-sanitize)
list(APPEND SYCL_TOOLCHAIN_DEPLOY_COMPONENTS sycl-sanitize)
endif()
if (TARGET sycl-trace)
list(APPEND SYCL_TOOLCHAIN_DEPLOY_COMPONENTS sycl-trace)
endif()

if(OpenCL_INSTALL_KHRONOS_ICD_LOADER AND TARGET OpenCL-ICD)
list(APPEND SYCL_TOOLCHAIN_DEPLOY_COMPONENTS OpenCL-ICD)
endif()
Expand Down
6 changes: 5 additions & 1 deletion sycl/tools/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
add_subdirectory(sycl-ls)

if (SYCL_ENABLE_XPTI_TRACING)
add_subdirectory(pi-trace)
if (UNIX)
add_subdirectory(sycl-prof)
add_subdirectory(sycl-trace)
add_subdirectory(sycl-sanitize)
endif()
endif()

# TODO: move each tool in its own sub-directory
Expand Down
14 changes: 0 additions & 14 deletions sycl/tools/pi-trace/CMakeLists.txt

This file was deleted.

36 changes: 36 additions & 0 deletions sycl/tools/sycl-prof/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
add_executable(sycl-prof
main.cpp
)

target_include_directories(sycl-prof PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/../xpti_helpers/"
)

target_link_libraries(sycl-prof PRIVATE
LLVMSupport
)

target_compile_options(sycl-prof PRIVATE -fno-exceptions -fno-rtti)

add_library(sycl_profiler_collector SHARED collector.cpp)
target_compile_definitions(sycl_profiler_collector PRIVATE XPTI_CALLBACK_API_EXPORTS)
target_link_libraries(sycl_profiler_collector PRIVATE xptifw)
if (TARGET OpenCL-Headers)
target_link_libraries(sycl_profiler_collector PRIVATE OpenCL-Headers)
endif()
target_include_directories(sycl_profiler_collector PRIVATE
"${sycl_inc_dir}"
"${sycl_src_dir}"
)

target_compile_options(sycl_profiler_collector PRIVATE -g)

add_dependencies(sycl-prof sycl_profiler_collector)
add_dependencies(sycl-toolchain sycl-prof)

include(GNUInstallDirs)
install(TARGETS sycl-prof sycl_profiler_collector
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sycl-prof
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sycl-prof
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sycl-prof
)
152 changes: 152 additions & 0 deletions sycl/tools/sycl-prof/collector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//==-------------- collector.cpp -------------------------------------------==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "writer.hpp"
#include "xpti/xpti_data_types.h"

#include <cstdint>
#include <xpti/xpti_trace_framework.h>

#include <chrono>
#include <memory>
#include <stdio.h>
#include <thread>
#include <unistd.h>

unsigned long process_id() { return static_cast<unsigned long>(getpid()); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why unsigned long?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is very Linux-specific. I'll have to come up with a Windows variant, though.


namespace chrono = std::chrono;

Writer *GWriter = nullptr;

XPTI_CALLBACK_API void piBeginEndCallback(uint16_t TraceType,
xpti::trace_event_data_t *,
xpti::trace_event_data_t *,
uint64_t /*Instance*/,
const void *UserData);
XPTI_CALLBACK_API void taskBeginEndCallback(uint16_t TraceType,
xpti::trace_event_data_t *,
xpti::trace_event_data_t *,
uint64_t /*Instance*/,
const void *UserData);
XPTI_CALLBACK_API void waitBeginEndCallback(uint16_t TraceType,
xpti::trace_event_data_t *,
xpti::trace_event_data_t *,
uint64_t /*Instance*/,
const void *UserData);

XPTI_CALLBACK_API void xptiTraceInit(unsigned int /*major_version*/,
unsigned int /*minor_version*/,
const char * /*version_str*/,
const char *StreamName) {
if (GWriter == nullptr) {
GWriter = new JSONWriter(std::getenv("SYCL_PROF_OUT_FILE"));
GWriter->init();
}

if (std::string_view(StreamName) == "sycl.pi") {
uint8_t StreamID = xptiRegisterStream(StreamName);
xptiRegisterCallback(StreamID,
(uint16_t)xpti::trace_point_type_t::function_begin,
piBeginEndCallback);
xptiRegisterCallback(StreamID,
(uint16_t)xpti::trace_point_type_t::function_end,
piBeginEndCallback);
} else if (std::string_view(StreamName) == "sycl") {
uint8_t StreamID = xptiRegisterStream(StreamName);
xptiRegisterCallback(StreamID,
(uint16_t)xpti::trace_point_type_t::task_begin,
taskBeginEndCallback);
xptiRegisterCallback(StreamID, (uint16_t)xpti::trace_point_type_t::task_end,
taskBeginEndCallback);
xptiRegisterCallback(StreamID,
(uint16_t)xpti::trace_point_type_t::wait_begin,
waitBeginEndCallback);
xptiRegisterCallback(StreamID, (uint16_t)xpti::trace_point_type_t::wait_end,
waitBeginEndCallback);
xptiRegisterCallback(StreamID,
(uint16_t)xpti::trace_point_type_t::barrier_begin,
waitBeginEndCallback);
xptiRegisterCallback(StreamID,
(uint16_t)xpti::trace_point_type_t::barrier_end,
waitBeginEndCallback);
}
}

XPTI_CALLBACK_API void xptiTraceFinish(const char *) { GWriter->finalize(); }

XPTI_CALLBACK_API void piBeginEndCallback(uint16_t TraceType,
xpti::trace_event_data_t *,
xpti::trace_event_data_t *,
uint64_t /*Instance*/,
const void *UserData) {
unsigned long TID = std::hash<std::thread::id>{}(std::this_thread::get_id());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not what is standard https://en.cppreference.com/w/cpp/utility/hash instead of unsigned long?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Standard type is size_t, made a change

unsigned long PID = process_id();
auto Now = chrono::high_resolution_clock::now();
auto TS = chrono::time_point_cast<chrono::nanoseconds>(Now)
.time_since_epoch()
.count();
if (TraceType == (uint16_t)xpti::trace_point_type_t::function_begin) {
GWriter->writeBegin(static_cast<const char *>(UserData), "Plugin", PID, TID,
TS);
} else {
GWriter->writeEnd(static_cast<const char *>(UserData), "Plugin", PID, TID,
TS);
}
}

XPTI_CALLBACK_API void taskBeginEndCallback(uint16_t TraceType,
xpti::trace_event_data_t *,
xpti::trace_event_data_t *Event,
uint64_t /*Instance*/,
const void *) {
unsigned long TID = std::hash<std::thread::id>{}(std::this_thread::get_id());
unsigned long PID = process_id();

std::string_view Name = "unknown";

xpti::metadata_t *Metadata = xptiQueryMetadata(Event);
for (auto &Item : *Metadata) {
std::string_view Key{xptiLookupString(Item.first)};
if (Key == "kernel_name" || Key == "memory_object") {
Name = xptiLookupString(Item.second);
}
}

auto Now = chrono::high_resolution_clock::now();
auto TS = chrono::time_point_cast<chrono::nanoseconds>(Now)
.time_since_epoch()
.count();

if (TraceType == (uint16_t)xpti::trace_point_type_t::task_begin) {
GWriter->writeBegin(Name, "SYCL", PID, TID, TS);
} else {
GWriter->writeEnd(Name, "SYCL", PID, TID, TS);
}
}

XPTI_CALLBACK_API void waitBeginEndCallback(uint16_t TraceType,
xpti::trace_event_data_t *,
xpti::trace_event_data_t *,
uint64_t /*Instance*/,
const void *UserData) {
unsigned long TID = std::hash<std::thread::id>{}(std::this_thread::get_id());
unsigned long PID = process_id();
auto Now = chrono::high_resolution_clock::now();
auto TS = chrono::time_point_cast<chrono::nanoseconds>(Now)
.time_since_epoch()
.count();
if (TraceType == (uint16_t)xpti::trace_point_type_t::wait_begin ||
TraceType == (uint16_t)xpti::trace_point_type_t::barrier_begin) {
GWriter->writeBegin(static_cast<const char *>(UserData), "SYCL", PID, TID,
TS);
} else {
GWriter->writeEnd(static_cast<const char *>(UserData), "SYCL", PID, TID,
TS);
}
}
69 changes: 69 additions & 0 deletions sycl/tools/sycl-prof/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//==------------ main.cpp - SYCL Profiler Tool -----------------------------==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "launch.hpp"
#include "llvm/Support/CommandLine.h"

#include <iostream>
#include <string>

using namespace llvm;

enum OutputFormatKind { JSON };

int main(int argc, char **argv, char *env[]) {
cl::opt<OutputFormatKind> OutputFormat(
"format", cl::desc("Set profiler output format:"),
cl::values(
// TODO performance summary
clEnumValN(JSON, "json",
"JSON file, compatible with chrome://tracing")));
cl::opt<std::string> OutputFilename("o", cl::desc("Specify output filename"),
cl::value_desc("filename"), cl::Required);
cl::opt<std::string> TargetExecutable(
cl::Positional, cl::desc("<target executable>"), cl::Required);
cl::list<std::string> Argv(cl::ConsumeAfter,
cl::desc("<program arguments>..."));

cl::ParseCommandLineOptions(argc, argv);

std::vector<const char *> NewEnv;

{
size_t I = 0;
while (env[I] != nullptr)
NewEnv.push_back(env[I++]);
}

std::string ProfOutFile = "SYCL_PROF_OUT_FILE=" + OutputFilename;
NewEnv.push_back(ProfOutFile.c_str());
NewEnv.push_back("XPTI_FRAMEWORK_DISPATCHER=libxptifw.so");
NewEnv.push_back("XPTI_SUBSCRIBERS=libsycl_profiler_collector.so");
NewEnv.push_back("XPTI_TRACE_ENABLE=1");
NewEnv.push_back(nullptr);

std::vector<const char *> Args;

Args.push_back(TargetExecutable.c_str());

for (auto &Arg : Argv) {
Args.push_back(Arg.c_str());
}

Args.push_back(nullptr);

int Err = launch(TargetExecutable.c_str(), Args, NewEnv);

if (Err) {
std::cerr << "Failed to launch target application. Error code " << Err
<< "\n";
return Err;
}

return 0;
}
89 changes: 89 additions & 0 deletions sycl/tools/sycl-prof/writer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//==----------------- writer.hpp -------------------------------------------==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#pragma once

#include <fstream>
#include <mutex>
#include <string_view>

class Writer {
public:
virtual void init() = 0;
virtual void finalize() = 0;
virtual void writeBegin(std::string_view Name, std::string_view Category,
unsigned long PID, unsigned long TID,
unsigned long TimeStamp) = 0;
virtual void writeEnd(std::string_view Name, std::string_view Category,
unsigned long PID, unsigned long TID,
unsigned long TimeStamp) = 0;
virtual ~Writer() = default;
};

class JSONWriter : public Writer {
public:
explicit JSONWriter(const std::string &OutPath) : MOutFile(OutPath) {}

void init() final {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you worried by your neighbors with these final? :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choosing between override and final I figured final is more suitable here...

std::lock_guard _{MWriteMutex};

MOutFile << "{\n";
MOutFile << " \"traceEvents\": [\n";
}

void writeBegin(std::string_view Name, std::string_view Category,
unsigned long PID, unsigned long TID,
unsigned long TimeStamp) override {
std::lock_guard _{MWriteMutex};

if (!MOutFile.is_open())
return;

MOutFile << "{\"name\": \"" << Name << "\", ";
MOutFile << "\"cat\": \"" << Category << "\", ";
MOutFile << "\"ph\": \"B\", ";
MOutFile << "\"pid\": \"" << PID << "\", ";
MOutFile << "\"tid\": \"" << TID << "\", ";
MOutFile << "\"ts\": \"" << TimeStamp << "\"},";
MOutFile << std::endl;
}

void writeEnd(std::string_view Name, std::string_view Category,
unsigned long PID, unsigned long TID,
unsigned long TimeStamp) override {
std::lock_guard _{MWriteMutex};

if (!MOutFile.is_open())
return;

MOutFile << "{\"name\": \"" << Name << "\", ";
MOutFile << "\"cat\": \"" << Category << "\", ";
MOutFile << "\"ph\": \"E\", ";
MOutFile << "\"pid\": \"" << PID << "\", ";
MOutFile << "\"tid\": \"" << TID << "\", ";
MOutFile << "\"ts\": \"" << TimeStamp << "\"},";
MOutFile << std::endl;
}

void finalize() final {
std::lock_guard _{MWriteMutex};

if (!MOutFile.is_open())
return;

MOutFile << "],\n";
MOutFile << "\"displayTimeUnit\":\"ns\"\n}\n";
MOutFile.close();
}

~JSONWriter() { finalize(); }

private:
std::mutex MWriteMutex;
std::ofstream MOutFile;
};
Loading