Skip to content

Commit

Permalink
Added support for TraceLogging providers
Browse files Browse the repository at this point in the history
Only strictly necessary for pre-Windows 8 operating systems as the
xperf from the Windows 10 Anniversary Edition version (August 2016)
SDK supports the proper translation "out of the box".

The algorithm to translate from TraceLogging provider name to GUID is
based on the C# code posted by Doug Cook to
https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/
  • Loading branch information
mwinterb authored and randomascii committed Mar 8, 2017
1 parent 311e038 commit a68459b
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 1 deletion.
6 changes: 6 additions & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ associated with each one.
UIforETW has some features that are specific to Chrome developers - but
it is fully functional for non-Chrome developers as well.

When adding TraceLogging and EventSource providers, UIforETW supports
the same *Name naming convention as other ETW tools. See
https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/
to ensure that your provider name and GUID conform to the convention.
EventSource providers should automatically match.

If you want to use UIforETW then you should download the latest
etwpackage.zip from the releases page at:
https://github.com/google/UIforETW/releases
Expand Down
1 change: 1 addition & 0 deletions UIforETW/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ BOOL CSettings::OnInitDialog()
toolTip_.AddTool(&btExtraUserProviders_, L"Extra user providers, separated by '+', such as "
L"\n\"Microsoft-Windows-Audio+Microsoft-Windows-HttpLog\". See \"xperf -providers\" "
L"for the full list. "
L"TraceLogging and EventSource providers must be prefixed by '*'. "
L"Note that incorrect user providers will cause tracing to fail to start.");
toolTip_.AddTool(&btPerfCounters_, L"Arbitrary performance counters to be logged occasionally.");
toolTip_.AddTool(&btWSMonitoredProcesses_, L"Names of processes whose working sets will be "
Expand Down
231 changes: 231 additions & 0 deletions UIforETW/TraceLoggingSupport.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "stdafx.h"
#include "TraceLoggingSupport.h"
#include "Utility.h"

#include <bcrypt.h>

#include <vector>
#include <array>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#pragma comment(lib, "Bcrypt.lib")

static std::system_error MakeSystemErrorFromWin32Error(DWORD errorCode, const char* context)
{
std::error_code code(errorCode, std::system_category());
return std::system_error(code, context);
}
namespace
{
class SHA1HashProvider
{
struct secret_init
{
};
// this is just to force the destructor to be called
// after this constructor finishes, regardless of whether or not
// the calling constructor throws an exception
SHA1HashProvider(secret_init)
: algHandle_(nullptr)
, hashHandle_(nullptr)
{
}
public:
SHA1HashProvider(const SHA1HashProvider&) = delete;
SHA1HashProvider& operator=(const SHA1HashProvider&) = delete;

SHA1HashProvider()
: SHA1HashProvider(secret_init())
{
NTSTATUS error = BCryptOpenAlgorithmProvider(&algHandle_, BCRYPT_SHA1_ALGORITHM, nullptr, 0);
if (error != 0)
{
algHandle_ = nullptr;
throw MakeSystemErrorFromWin32Error(error, "BCryptOpenAlgorithmProvider");
}

DWORD objectLength;
DWORD cbData;

error = BCryptGetProperty(algHandle_, BCRYPT_OBJECT_LENGTH, reinterpret_cast<PUCHAR>(&objectLength), sizeof(objectLength), &cbData, 0);
if (error != 0)
{
hashHandle_ = nullptr;
throw MakeSystemErrorFromWin32Error(error, "BCryptGetProperty(BCRYPT_OBJECT_LENGTH)");
}

hashObject_.resize(objectLength);
error = BCryptCreateHash(algHandle_, &hashHandle_, hashObject_.data(), objectLength, nullptr, 0, 0);
if (error != 0)
{
hashHandle_ = nullptr;
throw MakeSystemErrorFromWin32Error(error, "BCryptCreateHash");
}
}
~SHA1HashProvider()
{
if (hashHandle_ != nullptr)
{
ATLVERIFY(BCryptDestroyHash(hashHandle_) == 0);
}

if (nullptr != algHandle_)
{
ATLVERIFY(BCryptCloseAlgorithmProvider(algHandle_, 0) == 0);
}
}

void AddBytesForHash(const unsigned char* blob, unsigned long bytes)
{
// ugh. how did an API written for an OS released in 2006 not consider constness?
// if this cast isn't safe, we'll crash as the namespace bytes should be in
// .rdata.
NTSTATUS error = BCryptHashData(hashHandle_, const_cast<unsigned char*>(blob), bytes, 0);
if (error != 0)
{
throw MakeSystemErrorFromWin32Error(error, "CryptHashData");
}
}
std::array<unsigned char, 20> FinishHash() const
{
std::array<unsigned char, 20> hashData;
NTSTATUS error = BCryptFinishHash(hashHandle_, hashData.data(), static_cast<ULONG>(hashData.size()), 0);
if (error != 0)
{
throw MakeSystemErrorFromWin32Error(error, "CryptGetHashParam");
}
return hashData;
}

private:
BCRYPT_ALG_HANDLE algHandle_;
BCRYPT_HASH_HANDLE hashHandle_;
std::vector<unsigned char> hashObject_;
};
}


// Intended to match .NET's ToUpperInvariant. Presumably
// "LOCALE_NAME_INVARIANT" in Win32 is a close enough proxy.
static std::wstring ToUpperInvariant(const std::wstring& mixed)
{
std::wstring uppered;
int destLength = static_cast<int>(mixed.size());
if (destLength == 0)
return mixed;

// destLength doesn't include the terminating NUL, so add it.
destLength++;
{
uppered.resize(destLength);
wchar_t* upperedbuf = &uppered[0];
const int result = ::LCMapStringEx(LOCALE_NAME_INVARIANT, LCMAP_UPPERCASE, mixed.c_str(),
-1, upperedbuf, destLength, nullptr, nullptr, 0);
const DWORD lastError = ::GetLastError();
if (result > 0)
{
uppered.resize(result - 1);
return uppered;
}
uppered.resize(0);
if (lastError != ERROR_INSUFFICIENT_BUFFER)
throw MakeSystemErrorFromWin32Error(lastError, "LCMapStringEx");
}

destLength = ::LCMapStringEx(LOCALE_NAME_INVARIANT, LCMAP_UPPERCASE, mixed.c_str(), -1, nullptr,
0, nullptr, nullptr, 0);
if (destLength == 0)
{
const DWORD lastError = ::GetLastError();
throw MakeSystemErrorFromWin32Error(lastError, "LCMapStringEx");
}

{
uppered.resize(destLength);
wchar_t* upperedbuf = &uppered[0];
const int result = ::LCMapStringEx(LOCALE_NAME_INVARIANT, LCMAP_UPPERCASE, mixed.c_str(),
-1, upperedbuf, destLength, nullptr, nullptr, 0);
const DWORD lastError = ::GetLastError();
if (result > 0)
{
uppered.resize(result - 1);
return uppered;
}
throw MakeSystemErrorFromWin32Error(lastError, "LCMapStringEx");
}
}


static std::vector<unsigned char> GetBigEndianBytes(const std::wstring& str)
{
std::vector<unsigned char> bytes(str.size() * 2);

for (size_t i = 0; i < str.size(); i++)
{
wchar_t u = str[i];
bytes[i * 2] = u >> 8;
bytes[i * 2 + 1] = u & 0xFF;
}

return bytes;
}


// Adapted from the C# here,
// https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/
// Which just has
// /*
// This sample code is public - domain and may be used for any purpose.
// It is provided without warrantee, express or implied.
// */
// for licensing information.
std::wstring TraceLoggingProviderNameToGUID(const std::wstring& provider)
{
const std::vector<unsigned char> providerNameBytes = GetBigEndianBytes(ToUpperInvariant(provider));

static constexpr std::array<unsigned char, 16> namespaceBytes =
{
0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
};

SHA1HashProvider hasher;
hasher.AddBytesForHash(namespaceBytes.data(), static_cast<unsigned long>(namespaceBytes.size()));
hasher.AddBytesForHash(providerNameBytes.data(), static_cast<unsigned long>(providerNameBytes.size()));

auto hashBytes = hasher.FinishHash();
// Guid = Hash[0..15], with Hash[7] tweaked according to RFC 4122
hashBytes[7] = ((hashBytes[7] & 0x0F) | 0x50);
GUID guidBytes;
memcpy(&guidBytes, hashBytes.data(), sizeof(guidBytes));

return stringPrintf(L"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
guidBytes.Data1, guidBytes.Data2, guidBytes.Data3,
guidBytes.Data4[0], guidBytes.Data4[1], guidBytes.Data4[2],
guidBytes.Data4[3], guidBytes.Data4[4], guidBytes.Data4[5],
guidBytes.Data4[6], guidBytes.Data4[7]);

}




22 changes: 22 additions & 0 deletions UIforETW/TraceLoggingSupport.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#pragma once

#include <string>

extern std::wstring TraceLoggingProviderNameToGUID(const std::wstring& provider);

2 changes: 2 additions & 0 deletions UIforETW/UIforETW.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@
<ClInclude Include="Settings.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="TraceLoggingSupport.h" />
<ClInclude Include="Utility.h" />
<ClInclude Include="UIforETW.h" />
<ClInclude Include="UIforETWDlg.h" />
Expand All @@ -289,6 +290,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="Support.cpp" />
<ClCompile Include="TraceLoggingSupport.cpp" />
<ClCompile Include="Utility.cpp" />
<ClCompile Include="UIforETW.cpp" />
<ClCompile Include="UIforETWDlg.cpp" />
Expand Down
6 changes: 6 additions & 0 deletions UIforETW/UIforETW.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@
<ClInclude Include="VersionChecker.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="TraceLoggingSupport.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="UIforETW.cpp">
Expand Down Expand Up @@ -107,6 +110,9 @@
<ClCompile Include="VersionChecker.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TraceLoggingSupport.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="UIforETW.rc">
Expand Down
56 changes: 55 additions & 1 deletion UIforETW/UIforETWDlg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ limitations under the License.
#include "Utility.h"
#include "WorkingSet.h"
#include "Version.h"
#include "TraceLoggingSupport.h"

#include <algorithm>
#include <direct.h>
Expand Down Expand Up @@ -83,6 +84,48 @@ void CUIforETWDlg::vprintf(const wchar_t* pFormat, va_list args)
}


static std::wstring TranslateTraceLoggingProvider(const std::wstring& provider)
{
std::wstring providerOptions;
std::wstring justProviderName(provider);
auto endOfProvider = justProviderName.find(L':');
if (endOfProvider != std::wstring::npos)
{
providerOptions = justProviderName.substr(endOfProvider);
justProviderName.resize(endOfProvider);
}

std::wstring providerGUID = TraceLoggingProviderNameToGUID(justProviderName);

providerGUID += providerOptions;
return providerGUID;
}

static std::wstring TranslateUserModeProviders(const std::wstring& providers)
{
std::wstring translatedProviders;
translatedProviders.reserve(providers.size());
for (const auto& provider : split(providers, '+'))
{
if (provider.empty())
{
continue;
}
translatedProviders += '+';
if (provider.front() != '*')
{
translatedProviders += provider;
continue;
}
// if the provider name begins with a *, it follows the EventSource / TraceLogging
// convention and must be translated to a GUID.
// remove the leading '*' before calling the function
translatedProviders += TranslateTraceLoggingProvider(provider.substr(1));
}

return translatedProviders;
}

CUIforETWDlg::CUIforETWDlg(CWnd* pParent /*=NULL*/)
: CDialog(CUIforETWDlg::IDD, pParent)
, monitorThread_(this)
Expand Down Expand Up @@ -963,7 +1006,18 @@ void CUIforETWDlg::OnBnClickedStarttracing()
userProviders += L"+Microsoft-Windows-Kernel-Memory:0xE0";

if (!extraUserProviders_.empty())
userProviders += L"+" + extraUserProviders_;
{
try
{
userProviders += TranslateUserModeProviders(extraUserProviders_);
}
catch (const std::exception& e)
{
outputPrintf(L"Check the extra user providers; failed to translate them from the TraceLogging name to a GUID.\n%hs\n", e.what());
StopEventThreads();
return;
}
}

// DWM providers can be helpful also. Uncomment to enable.
//userProviders += L"+Microsoft-Windows-Dwm-Dwm";
Expand Down

0 comments on commit a68459b

Please sign in to comment.