Skip to content

Commit

Permalink
Merge pull request #59 from albertziegenhagel/pmc
Browse files Browse the repository at this point in the history
Add support for Performance Monitoring Counters (PMC)
  • Loading branch information
albertziegenhagel committed May 1, 2024
2 parents 33eb6e1 + 80b63e6 commit 6d03ff8
Show file tree
Hide file tree
Showing 69 changed files with 4,253 additions and 1,056 deletions.
16 changes: 15 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,21 @@ jobs:
- uses: actions/checkout@v4
with:
lfs: 'true'
lfs: 'false'

- name: Create LFS file list
shell: pwsh
run: git lfs ls-files -l | ForEach-Object { $_.split(" ")[0] } | Sort-Object | Out-File .lfs-assets-id

- name: Restore LFS cache
uses: actions/cache@v4
id: lfs-cache
with:
path: .git/lfs
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1

- name: Git LFS Pull
run: git lfs pull

- uses: lukka/get-cmake@latest
with:
Expand Down
16 changes: 15 additions & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,21 @@ jobs:
- uses: actions/checkout@v4
with:
lfs: 'true'
lfs: 'false'

- name: Create LFS file list
shell: pwsh
run: git lfs ls-files -l | ForEach-Object { $_.split(" ")[0] } | Sort-Object | Out-File .lfs-assets-id

- name: Restore LFS cache
uses: actions/cache@v4
id: lfs-cache
with:
path: .git/lfs
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1

- name: Git LFS Pull
run: git lfs pull

- uses: lukka/get-cmake@latest
with:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test-data.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jobs:
with:
arch: x64

- name: Check PMC sources
run: xperf -pmcsources

- name: Configure
run: cmake -G Ninja ${{ github.workspace }}/tests/apps

Expand Down
108 changes: 106 additions & 2 deletions apps/etl_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

#include <snail/etl/parser/records/visual_studio/diagnostics_hub.hpp>

#include <snail/etl/parser/records/snail/profiler.hpp>

#include <snail/common/detail/dump.hpp>

using namespace snail;
Expand Down Expand Up @@ -138,6 +140,7 @@ struct options
bool show_stacks = false;
bool show_config_ex = false;
bool show_vs_diag = false;
bool show_snail = false;

std::optional<std::uint32_t> process_of_interest;
bool all_processes = false;
Expand Down Expand Up @@ -209,6 +212,10 @@ options parse_command_line(int argc, char* argv[]) // NOLINT(modernize-avoid-c-a
{
result.show_vs_diag = true;
}
else if(current_arg == "--snail")
{
result.show_snail = true;
}
else if(current_arg == "--pid")
{
if(argc <= arg_i + 1) print_error_and_exit(application_path, "Missing argument for --pid.");
Expand Down Expand Up @@ -300,6 +307,7 @@ constexpr std::string_view get_guid_provider_name(const common::guid& guid)
if(guid == etl::parser::image_id_guid ||
guid == etl::parser::system_config_ex_guid) return "XPerf";
if(guid == etl::parser::vs_diagnostics_hub_guid) return "VS";
if(guid == etl::parser::snail_profiler_guid) return "Snail";
return "Unknown";
}

Expand Down Expand Up @@ -437,8 +445,9 @@ int main(int argc, char* argv[])

counting_event_observer observer;

std::size_t sample_count = 0;
std::size_t stack_count = 0;
std::size_t sample_count = 0;
std::unordered_map<std::uint16_t, std::size_t> pmc_sample_count;
std::size_t stack_count = 0;

std::unordered_map<std::uint32_t, std::uint32_t> thread_to_process;

Expand Down Expand Up @@ -684,6 +693,77 @@ int main(int argc, char* argv[])

std::cout << std::format("@{} {:30}: thread {} count {} ip {:#018x}\n", header.timestamp, observer.current_event_name, event.thread_id(), event.count(), event.instruction_pointer());

if(options.dump_trace_headers) common::detail::dump_buffer(header.buffer, 0, header.buffer.size(), "header");
if(options.dump_events) common::detail::dump_buffer(event.buffer(), 0, event.buffer().size(), "event");
});
register_known_event_names<etl::parser::perfinfo_v2_pmc_counter_profile_event_view>(observer.known_group_event_names);
observer.register_event<etl::parser::perfinfo_v2_pmc_counter_profile_event_view>(
[&options, &observer, &pmc_sample_count, &thread_to_process]([[maybe_unused]] const etl::etl_file::header_data& file_header,
[[maybe_unused]] const etl::common_trace_header& header,
const etl::parser::perfinfo_v2_pmc_counter_profile_event_view& event)
{
assert(event.dynamic_size() == event.buffer().size());

auto process_id = thread_to_process.find(event.thread_id());
if(!options.all_processes && (process_id != thread_to_process.end() && process_id->second != options.process_of_interest)) return;

auto iter = pmc_sample_count.find(event.profile_source());
if(iter == pmc_sample_count.end())
{
pmc_sample_count[event.profile_source()] = 1;
}
else
{
++iter->second;
}

if(!options.show_samples) return;

if(should_ignore(options, observer.current_event_name)) return;

std::cout << std::format("@{} {:30}: thread {} source {} ip {:#018x}\n", header.timestamp, observer.current_event_name, event.thread_id(), event.profile_source(), event.instruction_pointer());

if(options.dump_trace_headers) common::detail::dump_buffer(header.buffer, 0, header.buffer.size(), "header");
if(options.dump_events) common::detail::dump_buffer(event.buffer(), 0, event.buffer().size(), "event");
});
register_known_event_names<etl::parser::perfinfo_v3_sampled_profile_interval_event_view>(observer.known_group_event_names);
observer.register_event<etl::parser::perfinfo_v3_sampled_profile_interval_event_view>(
[&options, &observer]([[maybe_unused]] const etl::etl_file::header_data& file_header,
[[maybe_unused]] const etl::common_trace_header& header,
const etl::parser::perfinfo_v3_sampled_profile_interval_event_view& event)
{
assert(event.dynamic_size() == event.buffer().size());

if(!options.show_perfinfo) return;

if(should_ignore(options, observer.current_event_name)) return;

std::cout << std::format("@{} {:30}: source {} new-interval {} old-interval {} source-name '{}'\n", header.timestamp, observer.current_event_name, event.source(), event.new_interval(), event.old_interval(), utf8::utf16to8(event.source_name()));

if(options.dump_trace_headers) common::detail::dump_buffer(header.buffer, 0, header.buffer.size(), "header");
if(options.dump_events) common::detail::dump_buffer(event.buffer(), 0, event.buffer().size(), "event");
});
register_known_event_names<etl::parser::perfinfo_v2_pmc_counter_config_event_view>(observer.known_group_event_names);
observer.register_event<etl::parser::perfinfo_v2_pmc_counter_config_event_view>(
[&options, &observer]([[maybe_unused]] const etl::etl_file::header_data& file_header,
[[maybe_unused]] const etl::common_trace_header& header,
const etl::parser::perfinfo_v2_pmc_counter_config_event_view& event)
{
assert(event.dynamic_size() == event.buffer().size());

if(!options.show_perfinfo) return;

if(should_ignore(options, observer.current_event_name)) return;

std::string names;
for(std::uint32_t i = 0; i < event.counter_count(); ++i)
{
if(i > 0) names.push_back(',');
names += std::format("'{}'", utf8::utf16to8(event.counter_name(i)));
}

std::cout << std::format("@{} {:30}: count {} names {}\n", header.timestamp, observer.current_event_name, event.counter_count(), names);

if(options.dump_trace_headers) common::detail::dump_buffer(header.buffer, 0, header.buffer.size(), "header");
if(options.dump_events) common::detail::dump_buffer(event.buffer(), 0, event.buffer().size(), "event");
});
Expand Down Expand Up @@ -953,12 +1033,36 @@ int main(int argc, char* argv[])
});
}

// Snail-Profiler
{
register_known_event_names<etl::parser::snail_profiler_profile_target_event_view>(observer.known_guid_event_names);
observer.register_event<etl::parser::snail_profiler_profile_target_event_view>(
[&options, &observer]([[maybe_unused]] const etl::etl_file::header_data& file_header,
const etl::common_trace_header& header,
const etl::parser::snail_profiler_profile_target_event_view& event)
{
assert(event.dynamic_size() == event.buffer().size());

if(!options.show_snail) return;
if(should_ignore(options, observer.current_event_name)) return;

std::cout << std::format("@{} {:30}: pid {}\n", header.timestamp, observer.current_event_name, event.process_id());

if(options.dump_trace_headers) common::detail::dump_buffer(header.buffer, 0, header.buffer.size(), "header");
if(options.dump_events) common::detail::dump_buffer(event.buffer(), 0, event.buffer().size(), "event");
});
}

std::cout << "\n";
file.process(observer);

std::cout << "\n";
std::cout << "Number of samples:\n";
std::cout << std::format(" Regular Profile: {}\n", sample_count);
for(const auto& [source, count] : pmc_sample_count)
{
std::cout << std::format(" PMC profile {}: {}\n", source, count);
}
std::cout << std::format("Number of stacks: {}\n", stack_count);

if(options.show_events_summary)
Expand Down
54 changes: 43 additions & 11 deletions apps/perf_data_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,22 +121,34 @@ int main(int argc, char* argv[])

perf_data::dispatching_event_observer observer;

struct
struct samples_range
{
std::size_t count = 0;

std::uint64_t first_time = std::numeric_limits<std::uint64_t>::max();
std::uint64_t last_time = std::numeric_limits<std::uint64_t>::min();
} samples_data;
};

std::unordered_map<std::optional<std::uint64_t>, samples_range> round_samples;
std::unordered_map<std::optional<std::uint64_t>, samples_range> total_samples;

const auto flush_samples = [&samples_data]()
const auto flush_samples = [&round_samples, &total_samples]()
{
if(samples_data.count == 0) return;
std::cout << std::format("SAMPLES @{}-{} count {}", samples_data.first_time, samples_data.last_time, samples_data.count) << "\n";
for(auto& [id, round_info] : round_samples)
{
if(round_info.count == 0) continue;
const auto id_str = id ? std::to_string(*id) : "null";
std::cout << std::format("SAMPLES @{}-{} id {} count {}", round_info.first_time, round_info.last_time, id_str, round_info.count) << "\n";

auto& total_info = total_samples[id];
total_info.count += round_info.count;
total_info.first_time = std::min(total_info.first_time, round_info.first_time);
total_info.last_time = std::max(total_info.last_time, round_info.last_time);

samples_data.count = 0;
samples_data.first_time = std::numeric_limits<std::uint64_t>::max();
samples_data.last_time = std::numeric_limits<std::uint64_t>::min();
round_info.count = 0;
round_info.first_time = std::numeric_limits<std::uint64_t>::max();
round_info.last_time = std::numeric_limits<std::uint64_t>::min();
}
};

observer.register_event<perf_data::parser::id_index_event_view>(
Expand Down Expand Up @@ -239,10 +251,30 @@ int main(int argc, char* argv[])
observer.register_event<perf_data::parser::sample_event>(
[&](const perf_data::parser::sample_event& event)
{
++samples_data.count;
samples_data.first_time = std::min(samples_data.first_time, *event.time);
samples_data.last_time = std::max(samples_data.last_time, *event.time);
auto& info = round_samples[event.id];
++info.count;
info.first_time = std::min(info.first_time, *event.time);
info.last_time = std::max(info.last_time, *event.time);
});

file.process(observer);

std::cout << "\n";
std::cout << "Total samples:\n";
for(const auto& [id, info] : total_samples)
{
auto id_name = id ? std::to_string(*id) : "null";
if(id)
{
for(const auto& desc : file.metadata().event_desc)
{
if(std::ranges::find(desc.ids, *id) != desc.ids.end())
{
id_name = std::format("{} ({})", desc.event_string, *id);
break;
}
}
}
std::cout << std::format(" id {} @{}-{} count {}", id_name, info.first_time, info.last_time, info.count) << "\n";
}
}
42 changes: 42 additions & 0 deletions apps/snail-profiler.manifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<instrumentationManifest
xsi:schemaLocation="http://schemas.microsoft.com/win/2004/08/events eventman.xsd"
xmlns="http://schemas.microsoft.com/win/2004/08/events"
xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:trace="http://schemas.microsoft.com/win/2004/08/events/trace">
<instrumentation>
<events>
<provider name="Snail-Profiler"
guid="{460B83B6-FC11-481B-B7AA-4038CA4C4C48}" symbol="SnailProfiler"
resourceFileName="." messageFileName=".">
<events>
<event symbol="ProfilingTarget" value="1" version="0"
level="win:Informational" template="ProfilingTargetTemplate"
message="$(string.Snail-Profiler.event.1.message)">
</event>
</events>
<levels>
</levels>
<templates>
<template tid="ProfilingTargetTemplate">
<data name="ProcessId" inType="win:UInt32" outType="xs:unsignedInt">
</data>
</template>
</templates>
</provider>
</events>
</instrumentation>
<localization>
<resources culture="en-US">
<stringTable>
<string id="level.Informational" value="Information">
</string>
<string id="Snail-Profiler.event.1.message"
value="Profiling target (%1).">
</string>
</stringTable>
</resources>
</localization>
</instrumentationManifest>
Loading

0 comments on commit 6d03ff8

Please sign in to comment.