forked from CobaltFusion/DebugViewPP
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDbgviewReader.cpp
More file actions
222 lines (192 loc) · 6.83 KB
/
Copy pathDbgviewReader.cpp
File metadata and controls
222 lines (192 loc) · 6.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// (C) Copyright Gert-Jan de Vos and Jan Wilmans 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "Win32/Win32Lib.h"
#include "CobaltFusion/stringbuilder.h"
#include "DebugViewppLib/PolledLogSource.h"
#include "DebugViewppLib/DbgviewReader.h"
#include "DebugViewppLib/LineBuffer.h"
#include <iomanip>
#include <string>
#include <thread>
namespace fusion {
namespace debugviewpp {
const std::string SysinternalsDebugViewAgentPort = "2020";
DbgviewReader::DbgviewReader(Timer& timer, ILineBuffer& linebuffer, const std::string& hostname) :
PolledLogSource(timer, SourceType::Pipe, linebuffer, 40),
m_hostname(hostname),
m_thread([this] { Loop(); })
{
SetDescription(wstringbuilder() << "Dbgview Agent at " << m_hostname);
}
std::vector<unsigned char> Read(std::stringstream& is, size_t amount)
{
if (amount < 1)
{
return std::vector<unsigned char>();
}
std::vector<unsigned char> buffer(amount);
is.read(reinterpret_cast<char*>(buffer.data()), amount);
return buffer;
}
namespace Magic {
const int ColumnnOneMark = 1;
const int ColumnnTwoMark = 2;
const int Base = 0x83050000;
const int CaptureKernelEnable = Base + 0x00; // 0
const int CaptureKernelDisable = Base + 0x04; // 1
const int VerboseKernelMessagesEnable = Base + 0x08; // 2 // Meaning of these 'VerboseKernel' values was never confirmed
const int VerboseKernelMessagesDisable = Base + 0x0C; // 3 //
const int PassThroughDisable = Base + 0x10; // 4
const int PassThroughEnable = Base + 0x14; // 5
const int CaptureWin32Enable = Base + 0x18; // 6
const int CaptureWin32Disable = Base + 0x1c; // 7
const int Unknown3 = Base + 0x20; // 8
const int RequestUnknown = Base + 0x24; // 9 // answer: 0x7fffffff
const int RequestQueryPerformanceFrequency = Base + 0x28; // A
const int Unknown4 = Base + 0x2C;
const int Unknown5 = Base + 0x30;
const int ForceCarriageReturnsEnable = Base + 0x34;
const int ForceCarriageReturnsDisable = Base + 0x38;
} // namespace Magic
template <typename T>
std::string ToHex(const T& s)
{
std::ostringstream result;
result << "[" << s.size() << "] ";
for (size_t i = 0; i < s.size(); ++i)
result << std::hex << std::setfill('0') << std::setw(2) << std::uppercase << (static_cast<int>(s[i]) & 0xff) << " ";
return result.str();
}
template <typename T>
std::string ToChar(const T& s)
{
std::ostringstream result;
result << "[" << s.size() << "] ";
for (size_t i = 0; i < s.size(); ++i)
{
if (s[i] > 32)
{
result << std::setfill(' ') << std::setw(2) << static_cast<char>(s[i]) << " ";
}
else
{
result << " . ";
}
}
return result.str();
}
void DbgviewReader::SetAutoNewLine(bool value)
{
LogSource::SetAutoNewLine(value);
// todo: send ForceCarriageReturnsEnable/ForceCarriageReturnsDisable to dbgview-agent
}
void DbgviewReader::Loop()
{
SetDescription(wstringbuilder() << "Dbgview Agent at " << m_hostname);
m_iostream.connect(m_hostname, SysinternalsDebugViewAgentPort);
const std::string processName("[tcp]");
Write<DWORD>(m_iostream, Magic::RequestQueryPerformanceFrequency);
auto qpFrequency = Read<DWORD>(m_iostream);
long long t0 = 0;
bool first = true;
if (!m_iostream || qpFrequency == 0)
{
LogSource::Add(stringbuilder() << "Unable to connect to " << GetDescription() << ", " << m_iostream.error().message());
Signal();
return;
}
Write<DWORD>(m_iostream, Magic::CaptureKernelEnable);
Write<DWORD>(m_iostream, Magic::VerboseKernelMessagesEnable);
Write<DWORD>(m_iostream, Magic::CaptureWin32Enable);
Write<DWORD>(m_iostream, Magic::PassThroughEnable);
if (GetAutoNewLine())
{
Write<DWORD>(m_iostream, Magic::ForceCarriageReturnsEnable);
}
else
{
Write<DWORD>(m_iostream, Magic::ForceCarriageReturnsDisable);
}
double timerUnit = 1. / qpFrequency;
AddMessage(stringbuilder() << "Connected to " << GetDescription());
Signal();
while (!AtEnd())
{
m_iostream.clear();
auto messageLength = Read<DWORD>(m_iostream);
if (m_iostream.eof())
{
AddMessage(stringbuilder() << "Connection to " << GetDescription() << " closed.");
LogSource::Abort();
Signal();
break;
}
if (!m_iostream || messageLength >= 0x7fffffff)
{
AddMessage(0, processName, "<error parsing messageLength>");
Signal();
break;
}
if (messageLength == 0)
{ // keep alive
continue;
}
// dont read from the tcp::iostream directly,
// instead use read() to receive the complete message.
// this allows us to use ss.tellg() to determine the amount of trash bytes.
std::vector<char> buffer(messageLength);
m_iostream.read(buffer.data(), messageLength);
std::stringstream ss(std::ios_base::in | std::ios_base::out | std::ios::binary);
ss.write(buffer.data(), buffer.size());
DWORD pid = 0;
std::string msg;
for (;;)
{
Read<DWORD>(ss); // lineNr
if (!ss)
{
break;
}
auto filetime = Read<FILETIME>(ss);
auto qpcTime = Read<long long>(ss);
if (first)
{
t0 = qpcTime;
first = false;
}
auto time = (qpcTime - t0) * timerUnit;
if (buffer[20] == Magic::ColumnnOneMark)
{
unsigned char c1 = 0;
unsigned char c2 = 0;
if (!((ss >> c1 >> pid >> c2) && c1 == Magic::ColumnnOneMark && c2 == Magic::ColumnnTwoMark))
{
AddMessage(0, processName, "<error parsing pid>");
break;
}
Read(ss, 1); // discard one leading space
}
std::getline(ss, msg, '\0');
msg.push_back('\n'); // newlines are never send as part of the message
AddMessage(time, filetime, pid, processName, msg);
// strangely, messages are always send in multiples of 4 bytes.
// this means depending on the message length there are 1, 2 or 3 trailing bytes of undefined data.
auto remainder = static_cast<int>(ss.tellg() % 4);
if (remainder > 0)
{
Read(ss, 4 - remainder);
}
}
Signal();
}
}
void DbgviewReader::Abort()
{
m_iostream.close();
LogSource::Abort();
m_thread.join();
}
} // namespace debugviewpp
} // namespace fusion