summaryrefslogtreecommitdiff
path: root/src/vm/eventpipeprotocolhelper.cpp
diff options
context:
space:
mode:
authorJosé Rivero <jorive@microsoft.com>2019-03-18 10:23:30 -0700
committerGitHub <noreply@github.com>2019-03-18 10:23:30 -0700
commit4497df34cbe3d8bf7a21828714a3cf764da16e98 (patch)
tree894fc03e621e498cf6b5b33a34fdbb3c63c7d10a /src/vm/eventpipeprotocolhelper.cpp
parentb88f2f635b02c130ae00b4d8aee3e766a60698d5 (diff)
downloadcoreclr-4497df34cbe3d8bf7a21828714a3cf764da16e98.tar.gz
coreclr-4497df34cbe3d8bf7a21828714a3cf764da16e98.tar.bz2
coreclr-4497df34cbe3d8bf7a21828714a3cf764da16e98.zip
[EventPipe] Adding an IPC server to handle out of process communication. (#23106)
This is the first commit to enable a "diagnostic port" using IPC (Named Pipe on Windows and Unix Domain Socket on other platforms). This change currently enable EventPipe to be enabled/disabled without the use of a file drop. - Split the DiagnosticsIpc into (DiagnosticsIpc/IpcStream) - DiagnosticsIpc (IPC listener) is meant to be used by the Diagnostic server. - IpcStream (IPC channel) is meant to be use to communicate with the connected client. - Change the FastSerializer dependency from `CFileStream` to `StreamWriter` This abstraction is meant decouple the writing of objects in order to extend its usability. The main objective is to reuse FastSerializer to stream data through the open IPC channel. - Moved the EventPipeSessionProvider* classes to their own file. - Added a more streamlined parsing achievable by defining a simpler binary protocol (by noahfalk). 1. Only one allocation is needed for the EventPipeProviderConfiguration array, no allocations or copies are needed for strings because we can refer to them directly out of the incoming command buffer 2. No change to the EventPipe API for enable is required. EventPipeProviderConfiguration retains its current behavior of not deleting the string pointers it holds. 3. No leaks happen because the command buffer owns the string memory and ensures that it stays alive for the duration of the Enable() call.
Diffstat (limited to 'src/vm/eventpipeprotocolhelper.cpp')
-rw-r--r--src/vm/eventpipeprotocolhelper.cpp156
1 files changed, 156 insertions, 0 deletions
diff --git a/src/vm/eventpipeprotocolhelper.cpp b/src/vm/eventpipeprotocolhelper.cpp
new file mode 100644
index 0000000000..bfcdcfa2f8
--- /dev/null
+++ b/src/vm/eventpipeprotocolhelper.cpp
@@ -0,0 +1,156 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "eventpipeprotocolhelper.h"
+#include "diagnosticsipc.h"
+#include "diagnosticsprotocol.h"
+
+#ifdef FEATURE_PERFTRACING
+
+bool EventPipeProtocolHelper::TryParseProviderConfigurations(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray<EventPipeProviderConfiguration> &result)
+{
+ // Picking an arbitrary upper bound,
+ // This should be larger than any reasonable client request.
+ const uint32_t MaxCountConfigs = 1000; // TODO: This might be too large.
+
+ uint32_t countConfigs = 0;
+ if (!TryParse(bufferCursor, bufferLen, countConfigs))
+ return false;
+ if (countConfigs > MaxCountConfigs)
+ return false;
+ EventPipeProviderConfiguration *pConfigs = result.AllocNoThrow(countConfigs);
+ if (pConfigs == nullptr)
+ return false;
+
+ for (uint32_t i = 0; i < countConfigs; i++)
+ {
+ uint64_t keywords = 0;
+ if (!TryParse(bufferCursor, bufferLen, keywords))
+ return false;
+
+ uint32_t logLevel = 0;
+ if (!TryParse(bufferCursor, bufferLen, logLevel))
+ return false;
+ if (logLevel > 5) // (logLevel > EventPipeEventLevel::Verbose)
+ return false;
+
+ LPCWSTR pProviderName = nullptr;
+ if (!TryParseString(bufferCursor, bufferLen, pProviderName))
+ return false;
+ if (wcslen(pProviderName) == 0)
+ return false; // TODO: Should we ignore these input?
+
+ LPCWSTR pFilterData = nullptr; // This parameter is optional.
+ TryParseString(bufferCursor, bufferLen, pFilterData);
+
+ pConfigs[i] = EventPipeProviderConfiguration(pProviderName, keywords, logLevel, pFilterData);
+ }
+ return true;
+}
+
+void EventPipeProtocolHelper::EnableFileTracingEventHandler(IpcStream *pStream)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(pStream != nullptr);
+ }
+ CONTRACTL_END;
+
+ // TODO: Read within a loop.
+ const uint32_t BufferSize = 8192;
+ uint8_t buffer[BufferSize]{};
+ uint32_t nNumberOfBytesRead = 0;
+ bool fSuccess = pStream->Read(buffer, sizeof(buffer), nNumberOfBytesRead);
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ delete pStream;
+ return;
+ }
+
+ // The protocol buffer is defined as:
+ // X, Y, Z means encode bytes for X followed by bytes for Y followed by bytes for Z
+ // message = uint circularBufferMB, ulong multiFileTraceLength, string outputPath, array<provider_config> providers
+ // uint = 4 little endian bytes
+ // ulong = 8 little endian bytes
+ // wchar = 2 little endian bytes, UTF16 encoding
+ // array<T> = uint length, length # of Ts
+ // string = (array<char> where the last char must = 0) or (length = 0)
+ // provider_config = ulong keywords, uint logLevel, string provider_name, string filter_data
+
+ LPCWSTR strOutputPath;
+ uint32_t circularBufferSizeInMB = EventPipeProtocolHelper::DefaultCircularBufferMB;
+ uint64_t multiFileTraceLengthInSeconds = EventPipeProtocolHelper::DefaultMultiFileTraceLengthInSeconds;
+ CQuickArray<EventPipeProviderConfiguration> providerConfigs;
+
+ uint8_t *pBufferCursor = buffer;
+ uint32_t bufferLen = nNumberOfBytesRead;
+ if (!TryParse(pBufferCursor, bufferLen, circularBufferSizeInMB) ||
+ !TryParse(pBufferCursor, bufferLen, multiFileTraceLengthInSeconds) ||
+ !TryParseString(pBufferCursor, bufferLen, strOutputPath) ||
+ !TryParseProviderConfigurations(pBufferCursor, bufferLen, providerConfigs))
+ {
+ return; // TODO: error handling
+ }
+
+ EventPipeSessionID sessionId = (EventPipeSessionID) nullptr;
+ if (providerConfigs.Size() > 0)
+ {
+ sessionId = EventPipe::Enable(
+ strOutputPath, // outputFile
+ circularBufferSizeInMB, // circularBufferSizeInMB
+ DefaultProfilerSamplingRateInNanoseconds, // ProfilerSamplingRateInNanoseconds
+ providerConfigs.Ptr(), // pConfigs
+ static_cast<uint32_t>(providerConfigs.Size()), // numConfigs
+ multiFileTraceLengthInSeconds); // multiFileTraceLengthInSeconds
+ }
+
+ uint32_t nBytesWritten = 0;
+ fSuccess = pStream->Write(&sessionId, sizeof(sessionId), nBytesWritten);
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ delete pStream;
+ return;
+ }
+
+ fSuccess = pStream->Flush();
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+ delete pStream;
+}
+
+void EventPipeProtocolHelper::DisableTracingEventHandler(IpcStream *pStream)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(pStream != nullptr);
+ }
+ CONTRACTL_END;
+
+ uint32_t nNumberOfBytesRead = 0;
+ EventPipeSessionID sessionId = (EventPipeSessionID) nullptr;
+ const bool fSuccess = pStream->Read(&sessionId, sizeof(sessionId), nNumberOfBytesRead);
+ if (!fSuccess || nNumberOfBytesRead != sizeof(sessionId))
+ {
+ // TODO: Add error handling.
+ delete pStream;
+ return;
+ }
+
+ EventPipe::Disable(sessionId);
+ // TODO: Should we acknowledge back?
+ delete pStream;
+}
+
+#endif // FEATURE_PERFTRACING