Skip to content

Commit 51476af

Browse files
committed
wrapper utility for handling diagnostic messages
1 parent 003f140 commit 51476af

File tree

7 files changed

+166
-24
lines changed

7 files changed

+166
-24
lines changed

IntelPresentMon/PresentMonAPI2/PresentMonDiagnostics.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
extern "C" {
77
#endif
88

9+
// Debug Diagnostic Layer (EXPERIMENTAL)
10+
// this sub-library interface enables users of the PresentMon API
11+
// to get detail feedback about issues such as errors in API usage
12+
// or suboptimal usage patterns
13+
// It gives more information than can be communicated in error codes
14+
// and it gives feedback in situations where there are errors in API
15+
// usage but they are handled permissively rather than hard-failing
16+
917
enum PM_DIAGNOSTIC_LEVEL
1018
{
1119
PM_DIAGNOSTIC_LEVEL_NONE = 0,
@@ -62,8 +70,8 @@ extern "C" {
6270
{
6371
// ignore all messages greater than filterLevel
6472
PM_DIAGNOSTIC_LEVEL filterLevel;
65-
// bitmask of destinations to transmit diagnostics to
66-
PM_DIAGNOSTIC_OUTPUT_FLAGS outputFlags;
73+
// bitmask of PM_DIAGNOSTIC_OUTPUT_FLAGS destinations to transmit diagnostics to
74+
int outputFlags;
6775
// array of subsystems, ignore all not in this array (set as nullptr to accept all)
6876
PM_DIAGNOSTIC_SUBSYSTEM* pSubsystems;
6977
// number of elements in above array of subsystems
@@ -77,9 +85,9 @@ extern "C" {
7785
};
7886

7987
// all pmDiagnostic functions must NOT be invoked concurrently
80-
// the only exception is pmDiagnosticSignalWaiter which can be called concurrently with the other functions
81-
// from any number of threads
82-
// ideally, after setup is completed only one thread shfould be calling the get/dequeue/wait functions
88+
// the only exceptions are pmDiagnosticSignalWaiter which can be called concurrently with the other functions
89+
// from any number of threads, and pmDiagnosticEnqueueMessage similarly
90+
// ideally, after setup is completed only one thread should be calling the get/dequeue/wait functions
8391

8492
// initialize and configure the diagnostic system; passing in nullptr yield default config
8593
PRESENTMON_API2_EXPORT PM_STATUS pmDiagnosticSetup(const PM_DIAGNOSTIC_CONFIGURATION* pConfig);
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#include "DiagnosticHandler.h"
2+
#include "../PresentMonAPIWrapperCommon/Exception.h"
3+
#include <thread>
4+
#include <atomic>
5+
#include <semaphore>
6+
#include <chrono>
7+
8+
namespace pmapi
9+
{
10+
namespace
11+
{
12+
class DiagnosticHandlerImpl
13+
{
14+
public:
15+
DiagnosticHandlerImpl(std::function<void(const PM_DIAGNOSTIC_MESSAGE&)> callback)
16+
{
17+
if (callback) {
18+
worker_ = std::jthread{ [this, callback = std::move(callback)] {
19+
try {
20+
constructionSema_.release();
21+
while (auto res = pmDiagnosticWaitForMessage(0)) {
22+
if (res == PM_DIAGNOSTIC_WAKE_REASON_MESSAGE_AVAILABLE) {
23+
PM_DIAGNOSTIC_MESSAGE* pMsg = nullptr;
24+
pmDiagnosticDequeueMessage(&pMsg);
25+
if (pMsg) {
26+
callback(*pMsg);
27+
pmDiagnosticFreeMessage(pMsg);
28+
}
29+
}
30+
else {
31+
break;
32+
}
33+
}
34+
}
35+
catch (...) {}
36+
} };
37+
// block caller until the worker thread is up and running
38+
constructionSema_.acquire();
39+
}
40+
}
41+
~DiagnosticHandlerImpl()
42+
{
43+
pmDiagnosticUnblockWaitingThread();
44+
}
45+
private:
46+
std::binary_semaphore constructionSema_{ 0 };
47+
std::jthread worker_;
48+
};
49+
}
50+
DiagnosticHandler::DiagnosticHandler(
51+
PM_DIAGNOSTIC_LEVEL filterLevel,
52+
int outputFlags,
53+
std::function<void(const PM_DIAGNOSTIC_MESSAGE&)> callback,
54+
std::span<PM_DIAGNOSTIC_SUBSYSTEM> allowList,
55+
bool enableTimestamp,
56+
bool enableTrace,
57+
bool enableLocation,
58+
int gracePeriodMs
59+
) : gracePeriodMs_{ gracePeriodMs } {
60+
const PM_DIAGNOSTIC_CONFIGURATION config{
61+
.filterLevel = filterLevel,
62+
.outputFlags = outputFlags,
63+
.pSubsystems = allowList.data(),
64+
.nSubsystems = (uint32_t)allowList.size(),
65+
.enableTimestamp = enableTimestamp,
66+
.enableTrace = enableTrace,
67+
.enableLocation = enableLocation,
68+
};
69+
if (pmDiagnosticSetup(&config) != PM_STATUS_SUCCESS) {
70+
throw Exception{ "Failure setting up diagnostic layer" };
71+
}
72+
pImpl = std::make_shared<DiagnosticHandlerImpl>(std::move(callback));
73+
}
74+
DiagnosticHandler::~DiagnosticHandler()
75+
{
76+
using namespace std::chrono_literals;
77+
std::this_thread::sleep_for(gracePeriodMs_ * 1ms);
78+
}
79+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#pragma once
2+
#include "../PresentMonAPI2/PresentMonDiagnostics.h"
3+
#include <span>
4+
#include <functional>
5+
#include <memory>
6+
7+
namespace pmapi
8+
{
9+
// DiagnosticHandler is simplifies the process of setting up the debug diagnostic layer
10+
// when you supply a callback, this class will spawn a thread to process messages from
11+
// the diagnostic queue (sleeping while none are available)
12+
// make sure that any resources accessed by this callback are done so in a thread-safe manner
13+
// and refrain from retaining references to the message as it is destroyed after handling
14+
class DiagnosticHandler
15+
{
16+
public:
17+
DiagnosticHandler(
18+
// ignore all messages greater than filterLevel
19+
PM_DIAGNOSTIC_LEVEL filterLevel = PM_DIAGNOSTIC_LEVEL_WARNING,
20+
// bitmask of destinations to transmit diagnostics to
21+
int outputFlags = PM_DIAGNOSTIC_OUTPUT_FLAGS_DEBUGGER,
22+
// callback that will process messages received into the queue
23+
std::function<void(const PM_DIAGNOSTIC_MESSAGE&)> callback = {},
24+
// span of subsystems to allow, ignore all not in this span (empty span to accept all)
25+
std::span<PM_DIAGNOSTIC_SUBSYSTEM> allowList = {},
26+
// capture timestamps as a string and add to all non-queue outputs
27+
bool enableTimestamp = true,
28+
// capture stack traces as a string when available (typically for error-level messages)
29+
bool enableTrace = false,
30+
// capture source file and line number as a string
31+
bool enableLocation = false,
32+
// add a grace period to wait for final messages before destroying diagnostics (ms)
33+
int gracePeriodMs = 50
34+
);
35+
~DiagnosticHandler();
36+
private:
37+
std::shared_ptr<void> pImpl;
38+
int gracePeriodMs_;
39+
};
40+
}

IntelPresentMon/PresentMonAPIWrapper/FixedQuery.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#include <cassert>
1313

1414

15-
// The FrameQueryContainer gives a simple way to define/register/poll queries and access the results in the specific
15+
// The FixedQueryContainer gives a simple way to define/register/poll queries and access the results in the specific
1616
// use case where you have a fixed set of metrics you want to poll that is known at compile-time
1717
// Example usage for dynamic query:
1818

IntelPresentMon/PresentMonAPIWrapper/PresentMonAPIWrapper.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
</ItemGroup>
1313
<ItemGroup>
1414
<ClCompile Include="BlobContainer.cpp" />
15+
<ClCompile Include="DiagnosticHandler.cpp" />
1516
<ClCompile Include="DynamicQuery.cpp" />
1617
<ClCompile Include="FixedQuery.cpp" />
1718
<ClCompile Include="FrameQuery.cpp" />
@@ -21,6 +22,7 @@
2122
</ItemGroup>
2223
<ItemGroup>
2324
<ClInclude Include="BlobContainer.h" />
25+
<ClInclude Include="DiagnosticHandler.h" />
2426
<ClInclude Include="DynamicQuery.h" />
2527
<ClInclude Include="FrameQuery.h" />
2628
<ClInclude Include="PresentMonAPIWrapper.h" />

IntelPresentMon/PresentMonAPIWrapper/PresentMonAPIWrapper.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
<ClCompile Include="FixedQuery.cpp">
3737
<Filter>Source Files</Filter>
3838
</ClCompile>
39+
<ClCompile Include="DiagnosticHandler.cpp">
40+
<Filter>Source Files</Filter>
41+
</ClCompile>
3942
</ItemGroup>
4043
<ItemGroup>
4144
<ClInclude Include="PresentMonAPIWrapper.h">
@@ -62,5 +65,8 @@
6265
<ClInclude Include="StaticQuery.h">
6366
<Filter>Header Files</Filter>
6467
</ClInclude>
68+
<ClInclude Include="DiagnosticHandler.h">
69+
<Filter>Header Files</Filter>
70+
</ClInclude>
6571
</ItemGroup>
6672
</Project>

IntelPresentMon/SampleClient/LogDemo.cpp

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,27 @@
2020
#include "Verbose.h"
2121
#include "LogSetup.h"
2222
#include "../PresentMonAPI2/Internal.h"
23-
#include "../PresentMonAPI2/PresentMonDiagnostics.h"
23+
#include "../PresentMonAPIWrapper/DiagnosticHandler.h"
2424

2525
using namespace std::chrono_literals;
2626
using namespace pmon::util;
2727

2828
PM_DEFINE_EX(LogDemoException);
2929

30+
const char* GetLevelName(PM_DIAGNOSTIC_LEVEL lvl) {
31+
switch (lvl) {
32+
case PM_DIAGNOSTIC_LEVEL_NONE: return "None";
33+
case PM_DIAGNOSTIC_LEVEL_FATAL: return "Fatal";
34+
case PM_DIAGNOSTIC_LEVEL_ERROR: return "Error";
35+
case PM_DIAGNOSTIC_LEVEL_WARNING: return "Warning";
36+
case PM_DIAGNOSTIC_LEVEL_INFO: return "Info";
37+
case PM_DIAGNOSTIC_LEVEL_PERFORMANCE: return "Performance";
38+
case PM_DIAGNOSTIC_LEVEL_DEBUG: return "Debug";
39+
case PM_DIAGNOSTIC_LEVEL_VERBOSE: return "Verbose";
40+
default: return "Unknown";
41+
}
42+
}
43+
3044
void f2() {
3145
pmlog_error();
3246
}
@@ -45,12 +59,16 @@ void RunLogDemo(int mode)
4559
{
4660
p2sam::LogChannelManager zLogMan_;
4761
p2sam::ConfigureLogging();
48-
{
49-
PM_DIAGNOSTIC_CONFIGURATION cfg{};
50-
cfg.filterLevel = PM_DIAGNOSTIC_LEVEL_INFO;
51-
cfg.outputFlags = PM_DIAGNOSTIC_OUTPUT_FLAGS_DEBUGGER;
52-
pmDiagnosticSetup(&cfg);
53-
}
62+
63+
// example of setting up diagnostic layer and custom diagnostic message handling
64+
pmapi::DiagnosticHandler dh{
65+
PM_DIAGNOSTIC_LEVEL_INFO,
66+
PM_DIAGNOSTIC_OUTPUT_FLAGS_DEBUGGER | PM_DIAGNOSTIC_OUTPUT_FLAGS_QUEUE,
67+
[](const PM_DIAGNOSTIC_MESSAGE& msg) { std::cout <<
68+
std::format("[PMON {}] <{}> {}\n", GetLevelName(msg.level),
69+
msg.pTimestamp ? msg.pTimestamp : "", msg.pText);
70+
}
71+
};
5472

5573
pmapi::Session sesh;
5674

@@ -233,15 +251,4 @@ void RunLogDemo(int mode)
233251
else {
234252
std::cout << "unknown mode for log demo" << std::endl;
235253
}
236-
std::thread{ [&] {
237-
std::this_thread::sleep_for(2000ms);
238-
std::cout << "set me free" << std::endl;
239-
pmDiagnosticUnblockWaitingThread();
240-
} }.detach();
241-
while (pmDiagnosticWaitForMessage(0) == PM_DIAGNOSTIC_WAKE_REASON_MESSAGE_AVAILABLE) {
242-
PM_DIAGNOSTIC_MESSAGE* pMsg = nullptr;
243-
pmDiagnosticDequeueMessage(&pMsg);
244-
std::cout << pMsg->pText << std::endl;
245-
pmDiagnosticFreeMessage(pMsg);
246-
}
247254
}

0 commit comments

Comments
 (0)