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

Add GetProcessEnvironment command to diagnostics server #40556

Merged
merged 12 commits into from
Aug 14, 2020
68 changes: 67 additions & 1 deletion src/coreclr/src/vm/processdiagnosticsprotocolhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#ifdef FEATURE_PERFTRACING

static inline uint32_t GetStringLength(const WCHAR *&value)
static inline uint32_t GetStringLength(const WCHAR *value)
{
return static_cast<uint32_t>(wcslen(value) + 1);
}
Expand Down Expand Up @@ -47,6 +47,7 @@ uint16_t ProcessInfoPayload::GetSize()
// LPCWSTR CommandLine; -> 4 bytes + strlen * sizeof(WCHAR)
// LPCWSTR OS; -> 4 bytes + strlen * sizeof(WCHAR)
// LPCWSTR Arch; -> 4 bytes + strlen * sizeof(WCHAR)
// Array<LPCWSTR> Environment; -> 4 bytes + total bytes of strings
josalem marked this conversation as resolved.
Show resolved Hide resolved

S_UINT16 size = S_UINT16(0);
josalem marked this conversation as resolved.
Show resolved Hide resolved
size += sizeof(ProcessId);
Expand All @@ -67,6 +68,12 @@ uint16_t ProcessInfoPayload::GetSize()
S_UINT16(GetStringLength(Arch) * sizeof(WCHAR)) :
S_UINT16(0);

EnsureEnv();

size += sizeof(uint32_t);
size += S_UINT16(_nEnvEntries * sizeof(uint32_t));
size += S_UINT16(_nWchars * sizeof(WCHAR));

ASSERT(!size.IsOverflow());
return size.Value();
}
Expand Down Expand Up @@ -108,12 +115,68 @@ bool ProcessInfoPayload::Flatten(BYTE * &lpBuffer, uint16_t &cbSize)
if (fSuccess)
fSuccess &= TryWriteString(lpBuffer, cbSize, Arch);

// Array<LPCWSTR>
josalem marked this conversation as resolved.
Show resolved Hide resolved
memcpy(lpBuffer, &_nEnvEntries, sizeof(_nEnvEntries));
lpBuffer += sizeof(_nEnvEntries);
cbSize -= sizeof(_nEnvEntries);

LPCWSTR cursor = Environment;
for (uint32_t i = 0; i < _nEnvEntries; i++)
{
if (!fSuccess)
break;

uint32_t len = static_cast<uint32_t>(wcslen(cursor) + 1);
fSuccess &= TryWriteString(lpBuffer, cbSize, cursor);
cursor += len;
}

// Assert we've used the whole buffer we were given
ASSERT(cbSize == 0);

return fSuccess;
}

void ProcessInfoPayload::EnsureEnv()
josalem marked this conversation as resolved.
Show resolved Hide resolved
{
if (Environment != nullptr)
jander-msft marked this conversation as resolved.
Show resolved Hide resolved
return;

// env block is an array of strings of the form "key=value" delimited by null and terminated by null
// e.g., "key=value\0key=value\0\0";
LPWSTR envBlock = GetEnvironmentStringsW();
LPWSTR envCursor = envBlock;
// first calculate the buffer size
uint32_t nWchars = 0;
uint32_t nEntries = 0;
while(*envCursor != 0)
{
uint32_t len = static_cast<uint32_t>(wcslen(envCursor) + 1);
nEntries++;
nWchars += len;
envCursor += len;
}
LPWSTR tmpEnv = new WCHAR[nWchars + 1];
envCursor = envBlock;
LPWSTR payloadCursor = tmpEnv;

// copy out the Environment block
while(*envCursor != 0)
{
// len characters
uint32_t len = static_cast<uint32_t>(wcslen(envCursor) + 1);
wcscpy(payloadCursor, envCursor);
envCursor += len;
payloadCursor += len;
}
*payloadCursor = W('\0');
Environment = tmpEnv;
FreeEnvironmentStringsW(envBlock);

_nEnvEntries = nEntries;
_nWchars = nWchars;
}

void ProcessDiagnosticsProtocolHelper::GetProcessInfo(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream)
{
CONTRACTL
Expand Down Expand Up @@ -147,6 +210,9 @@ void ProcessDiagnosticsProtocolHelper::GetProcessInfo(DiagnosticsIpc::IpcMessage
// Get the cookie
payload.RuntimeCookie = DiagnosticsIpc::GetAdvertiseCookie_V1();

// Get the environment block
payload.EnsureEnv();

DiagnosticsIpc::IpcMessage ProcessInfoResponse;
const bool fSuccess = ProcessInfoResponse.Initialize(DiagnosticsIpc::GenericSuccessHeader, payload) ?
ProcessInfoResponse.Send(pStream) :
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/src/vm/processdiagnosticsprotocolhelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,13 @@ struct ProcessInfoPayload
LPCWSTR OS;
LPCWSTR Arch;
GUID RuntimeCookie;
NewArrayHolder<const WCHAR> Environment = nullptr;
void EnsureEnv();
uint16_t GetSize();
bool Flatten(BYTE * &lpBuffer, uint16_t& cbSize);
private:
uint32_t _nEnvEntries = 0;
uint32_t _nWchars = 0;
};

class ProcessDiagnosticsProtocolHelper
Expand Down
35 changes: 34 additions & 1 deletion src/tests/tracing/eventpipe/processinfo/processinfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,18 @@ public static int Main(string[] args)
Process currentProcess = Process.GetCurrentProcess();
int pid = currentProcess.Id;
Logger.logger.Log($"Test PID: {pid}");
string envKey = "TESTKEY";
string envVal = "TESTVAL";
System.Environment.SetEnvironmentVariable(envKey, envVal);
josalem marked this conversation as resolved.
Show resolved Hide resolved
System.Environment.SetEnvironmentVariable("foo", "");
josalem marked this conversation as resolved.
Show resolved Hide resolved

Stream stream = ConnectionHelper.GetStandardTransport(pid);

// 0x04 = ProcessCommandSet, 0x00 = ProcessInfo
var processInfoMessage = new IpcMessage(0x04, 0x00);
Logger.logger.Log($"Wrote: {processInfoMessage}");
IpcMessage response = IpcClient.SendMessage(stream, processInfoMessage);
Logger.logger.Log($"Received: {response}");
Logger.logger.Log($"Received: <omitted>");

Utils.Assert(response.Header.CommandSet == 0xFF, $"Response must have Server command set. Expected: 0xFF, Received: 0x{response.Header.CommandSet:X2}"); // server
Utils.Assert(response.Header.CommandId == 0x00, $"Response must have OK command id. Expected: 0x00, Received: 0x{response.Header.CommandId:X2}"); // OK
Expand All @@ -93,6 +97,7 @@ public static int Main(string[] args)
// LPCWSTR CommandLine;
// LPCWSTR OS;
// LPCWSTR Arch;
// LPCWSTR Env;

int totalSize = response.Payload.Length;
Logger.logger.Log($"Total size of Payload == {totalSize} b");
Expand Down Expand Up @@ -191,6 +196,34 @@ public static int Main(string[] args)

Utils.Assert(expectedArchValue.Equals(arch), $"OS must match current Operating System. Expected: \"{expectedArchValue}\", Received: \"{arch}\"");

// VALIDATE ENV
// env block is an array of strings of the form "key=value" delimited by null
// e.g., "key=value\0key=value\0";
start = end;
end = start + 4 /* sizeof(uint32_t) */;
UInt32 envCount = BitConverter.ToUInt32(response.Payload[start..end]);
Logger.logger.Log($"envCount: {envCount}");

var env = new Dictionary<string,string>();
for (int i = 0; i < envCount; i++)
{
start = end;
end = start + 4 /* sizeof(uint32_t) */;
UInt32 pairLength = BitConverter.ToUInt32(response.Payload[start..end]);

start = end;
end = start + ((int)pairLength * sizeof(char));
Utils.Assert(end <= totalSize, $"String end can't exceed payload size. Expected: <{totalSize}, Received: {end} (decoded length: {pairLength})");
string envPair = System.Text.Encoding.Unicode.GetString(response.Payload[start..end]).TrimEnd('\0');
int equalsIndex = envPair.IndexOf('=');
env[envPair[0..equalsIndex]] = envPair[(equalsIndex+1)..];
}
Logger.logger.Log($"finished parsing env");


Utils.Assert(env.ContainsKey(envKey) && env[envKey].Equals(envVal), $"Did not find test environment key in the environment block.");
Logger.logger.Log($"Saw test values in env");

Utils.Assert(end == totalSize, $"Full payload should have been read. Expected: {totalSize}, Received: {end}");

Logger.logger.Log($"\n{{\n\tprocessId: {processId},\n\truntimeCookie: {runtimeCookie},\n\tcommandLine: {commandLine},\n\tOS: {OS},\n\tArch: {arch}\n}}");
Expand Down