Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug Report Tool] Report event viewer logs #11458

Merged
merged 7 commits into from
May 26, 2021
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
6 changes: 6 additions & 0 deletions .github/actions/spell-check/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ EUQ
evenodd
eventlog
everytime
evt
EWXFORCE
EWXFORCEIFHUNG
EWXLOGOFF
Expand Down Expand Up @@ -954,6 +955,7 @@ IVector
IView
IVirtual
IWeb
IXml
ixx
IZone
IZoom
Expand Down Expand Up @@ -1785,6 +1787,7 @@ sln
SMALLICON
SMTO
snd
snwprintf
softline
somil
Soref
Expand Down Expand Up @@ -1944,6 +1947,7 @@ THISCOMPONENT
thre
tif
TILEDWINDOW
timediff
TIMERID
timeunion
timeutil
Expand Down Expand Up @@ -2127,6 +2131,7 @@ webpack
webpage
website
wekyb
Wevtapi
Whichdoes
whitespaces
WIC
Expand Down Expand Up @@ -2156,6 +2161,7 @@ windowwalker
winerror
WINEVENT
winexe
winevt
winforms
winfx
winget
Expand Down
5 changes: 5 additions & 0 deletions tools/BugReportTool/BugReportTool/BugReportTool.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>Wevtapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\..\deps\cziplib\src\zip.c">
<WarningLevel>TurnOffAllWarnings</WarningLevel>
</ClCompile>
<ClCompile Include="EventViewer.cpp" />
<ClCompile Include="ReportMonitorInfo.cpp" />
<ClCompile Include="Main.cpp" />
<ClCompile Include="RegistryUtils.cpp" />
<ClCompile Include="XmlDocumentEx.cpp" />
<ClCompile Include="ZipTools\ZipFolder.cpp" />
</ItemGroup>
<ItemGroup>
Expand All @@ -52,9 +55,11 @@
<ItemGroup>
<ClInclude Include="..\..\..\deps\cziplib\src\miniz.h" />
<ClInclude Include="..\..\..\deps\cziplib\src\zip.h" />
<ClInclude Include="EventViewer.h" />
<ClInclude Include="ReportMonitorInfo.h" />
<ClInclude Include="..\..\..\common\utils\json.h" />
<ClInclude Include="RegistryUtils.h" />
<ClInclude Include="XmlDocumentEx.h" />
<ClInclude Include="ZipTools\ZipFolder.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<ClCompile Include="..\..\..\deps\cziplib\src\zip.c" />
<ClCompile Include="ReportMonitorInfo.cpp" />
<ClCompile Include="RegistryUtils.cpp" />
<ClCompile Include="EventViewer.cpp" />
<ClCompile Include="XmlDocumentEx.cpp" />
</ItemGroup>
<ItemGroup>
<Filter Include="ZipTools">
Expand All @@ -26,5 +28,7 @@
<ClInclude Include="..\..\..\deps\cziplib\src\zip.h" />
<ClInclude Include="ReportMonitorInfo.h" />
<ClInclude Include="RegistryUtils.h" />
<ClInclude Include="EventViewer.h" />
<ClInclude Include="XmlDocumentEx.h" />
</ItemGroup>
</Project>
189 changes: 189 additions & 0 deletions tools/BugReportTool/BugReportTool/EventViewer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#include "EventViewer.h"

#include <windows.h>
#include <sddl.h>
#include <stdio.h>
#include <winevt.h>
#include <fstream>
#include <common/utils/winapi_error.h>

#include "XmlDocumentEx.h"

namespace
{
std::vector<std::wstring> processes =
{
L"PowerToys.exe",
L"ColorPickerUI.exe",
L"PowerToys.Espresso.exe"
L"FancyZonesEditor.exe",
L"PowerToys.KeyboardManagerEngine.exe",
L"PowerToys.KeyboardManagerEditor.exe",
L"PowerLauncher.exe",
L"PowerToys.ShortcutGuide.exe"
};

// Batch size for number of events queried at once
constexpr int BATCH_SIZE = 50;

class EventViewerReporter
{
private:
// Report last 30 days
const long long PERIOD = 10 * 24 * 3600ll * 1000;

const std::wstring QUERY = L"<QueryList>" \
L" <Query Id='0'>" \
L" <Select Path='Application'>" \
L" *[System[TimeCreated[timediff(@SystemTime)&lt;%I64u]]] " \
L" and *[EventData[Data and (Data='%s')]]" \
L" </Select>" \
L" </Query>" \
L"</QueryList>";

std::wstring GetQuery(std::wstring processName)
{
wchar_t buff[1000];
memset(buff, 0, sizeof(buff));
_snwprintf_s(buff, sizeof(buff), QUERY.c_str(), PERIOD, processName.c_str());
return buff;
}

std::wofstream report;
EVT_HANDLE hResults;

void PrintEvent(EVT_HANDLE hEvent)
{
DWORD status = ERROR_SUCCESS;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD dwPropertyCount = 0;
LPWSTR pRenderedContent = NULL;

// The EvtRenderEventXml flag tells EvtRender to render the event as an XML string.
if (!EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount))
{
if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError()))
{
dwBufferSize = dwBufferUsed;
pRenderedContent = (LPWSTR)malloc(dwBufferSize);
if (pRenderedContent)
{
EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount);
}
}

if (ERROR_SUCCESS != (status = GetLastError()))
{
report << std::endl << L"EvtRender failed with " << get_last_error_or_default(GetLastError()) << std::endl << std::endl;
if (pRenderedContent)
{
free(pRenderedContent);
}
return;
}
}

XmlDocumentEx doc;
doc.LoadXml(pRenderedContent);
std::wstring formattedXml = L"";
try
{
formattedXml = doc.GetFormatedXml();
}
catch (...)
{
formattedXml = pRenderedContent;
}

report << std::endl << formattedXml << std::endl;
if (pRenderedContent)
{
free(pRenderedContent);
}
}

// Enumerate all the events in the result set.
void PrintResults(EVT_HANDLE hResults)
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hEvents[BATCH_SIZE];
DWORD dwReturned = 0;

while (true)
{
// Get a block of events from the result set.
if (!EvtNext(hResults, BATCH_SIZE, hEvents, INFINITE, 0, &dwReturned))
{
if (ERROR_NO_MORE_ITEMS != (status = GetLastError()))
{
report << L"EvtNext failed with " << status << std::endl;
}

break;
}

// For each event, call the PrintEvent function which renders the
// event for display. PrintEvent is shown in RenderingEvents.
for (DWORD i = 0; i < dwReturned; i++)
{
PrintEvent(hEvents[i]);
}
}

for (DWORD i = 0; i < dwReturned; i++)
{
if (nullptr != hEvents[i])
EvtClose(hEvents[i]);
}
}

public:
EventViewerReporter(const std::filesystem::path& tmpDir, std::wstring processName)
{
auto query = GetQuery(processName);
auto reportPath = tmpDir;
reportPath.append(L"EventViewer-" + processName + L".xml");
report = std::wofstream(reportPath);

hResults = EvtQuery(NULL, NULL, GetQuery(processName).c_str(), EvtQueryChannelPath);
if (NULL == hResults)
{
report << "Failed to report info for " << processName << ". " << get_last_error_or_default(GetLastError()) << std::endl;
return;
}
}

~EventViewerReporter()
{
if (hResults)
{
EvtClose(hResults);
hResults = nullptr;
}
}

void Report()
{
try
{
if (hResults)
{
PrintResults(hResults);
}
}
catch (...)
{
report << "Failed to report info" << std::endl;
}
}
};
}

void EventViewer::ReportEventViewerInfo(const std::filesystem::path& tmpDir)
{
for (auto& process : processes)
{
EventViewerReporter(tmpDir, process).Report();
}
}
7 changes: 7 additions & 0 deletions tools/BugReportTool/BugReportTool/EventViewer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once
#include <filesystem>

namespace EventViewer
{
void ReportEventViewerInfo(const std::filesystem::path& tmpDir);
}
5 changes: 5 additions & 0 deletions tools/BugReportTool/BugReportTool/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#include "ReportMonitorInfo.h"
#include "RegistryUtils.h"
#include "EventViewer.h"

using namespace std;
using namespace std::filesystem;
using namespace winrt::Windows::Data::Json;
Expand Down Expand Up @@ -331,6 +333,9 @@ int wmain(int argc, wchar_t* argv[], wchar_t*)
// Write compatibility tab info to the temporary folder
ReportCompatibilityTab(tmpDir);

// Write event viewer logs info to the temporary folder
EventViewer::ReportEventViewerInfo(tmpDir);

ReportBootstrapperLog(tmpDir);

// Zip folder
Expand Down
56 changes: 56 additions & 0 deletions tools/BugReportTool/BugReportTool/XmlDocumentEx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include "XmlDocumentEx.h"

#include <winrt/Windows.Foundation.Collections.h>

std::wstring XmlDocumentEx::GetFormatedXml()
{
stream.clear();
Print(FirstChild(), 0);
return stream.str();
}

void XmlDocumentEx::Print(winrt::Windows::Data::Xml::Dom::IXmlNode node, int indentation)
{
for (int i = 0; i < indentation; i++)
{
stream << " ";
}

PrintTagWithAttributes(node);
if (!node.HasChildNodes())
{
stream << L"<\\" << node.NodeName().c_str() << ">" << std::endl;
return;
}

if (node.ChildNodes().Size() == 1 && !node.FirstChild().HasChildNodes())
{
stream << node.InnerText().c_str() << L"<\\" << node.NodeName().c_str() << ">" << std::endl;
return;
}

stream << std::endl;
auto child = node.FirstChild();
do
{
Print(child, indentation + 2);
} while (child = child.NextSibling());

for (int i = 0; i < indentation; i++)
{
stream << " ";
}
stream << L"<\\" << node.NodeName().c_str() << ">" << std::endl;
}

void XmlDocumentEx::PrintTagWithAttributes(winrt::Windows::Data::Xml::Dom::IXmlNode node)
{
stream << L"<" << node.NodeName().c_str();
for (int i = 0; i < (int)node.Attributes().Size(); i++)
{
auto attr = node.Attributes().GetAt(i);
stream << L" " << attr.NodeName().c_str() << L"='" << attr.InnerText().c_str() << L"'";
}

stream << L">";
}
13 changes: 13 additions & 0 deletions tools/BugReportTool/BugReportTool/XmlDocumentEx.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once
#include <winrt/Windows.Data.Xml.Dom.h>

class XmlDocumentEx : public winrt::Windows::Data::Xml::Dom::XmlDocument
{
private:
std::wstringstream stream;
void Print(winrt::Windows::Data::Xml::Dom::IXmlNode node, int indentation);
void PrintTagWithAttributes(winrt::Windows::Data::Xml::Dom::IXmlNode node);

public:
std::wstring GetFormatedXml();
};