MINIFICPP-1394 - Use stack allocated buffer for first event api call
Signed-off-by: Arpad Boda <aboda@apache.org>
This closes #928
diff --git a/extensions/windows-event-log/ConsumeWindowsEventLog.cpp b/extensions/windows-event-log/ConsumeWindowsEventLog.cpp
index 29cb131..db0f4b1 100644
--- a/extensions/windows-event-log/ConsumeWindowsEventLog.cpp
+++ b/extensions/windows-event-log/ConsumeWindowsEventLog.cpp
@@ -39,6 +39,7 @@
#include "core/ProcessContext.h"
#include "core/ProcessSession.h"
#include "Bookmark.h"
+#include "utils/Deleters.h"
#include "utils/gsl.h"
@@ -514,30 +515,36 @@
bool ConsumeWindowsEventLog::createEventRender(EVT_HANDLE hEvent, EventRender& eventRender) {
logger_->log_trace("Rendering an event");
- DWORD size = 0;
+ WCHAR stackBuffer[4096];
+ DWORD size = sizeof(stackBuffer);
+ using Deleter = utils::StackAwareDeleter<WCHAR, utils::FreeDeleter>;
+ std::unique_ptr<WCHAR, Deleter> buf{stackBuffer, Deleter{ stackBuffer }};
+
DWORD used = 0;
DWORD propertyCount = 0;
- EvtRender(NULL, hEvent, EvtRenderEventXml, size, 0, &used, &propertyCount);
- if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
- LOG_LAST_ERROR(EvtRender);
- return false;
+ if (!EvtRender(NULL, hEvent, EvtRenderEventXml, size, buf.get(), &used, &propertyCount)) {
+ if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
+ LOG_LAST_ERROR(EvtRender);
+ return false;
+ }
+ if (used > maxBufferSize_) {
+ logger_->log_error("Dropping event because it couldn't be rendered within %" PRIu64 " bytes.", maxBufferSize_);
+ return false;
+ }
+ size = used;
+ buf.reset((LPWSTR)malloc(size));
+ if (!buf) {
+ return false;
+ }
+ if (!EvtRender(NULL, hEvent, EvtRenderEventXml, size, buf.get(), &used, &propertyCount)) {
+ LOG_LAST_ERROR(EvtRender);
+ return false;
+ }
}
- if (used > maxBufferSize_) {
- logger_->log_error("Dropping event because it couldn't be rendered within %" PRIu64 " bytes.", maxBufferSize_);
- return false;
- }
+ logger_->log_debug("Event rendered with size %" PRIu32 ". Performing doc traversing...", used);
- size = used;
- std::vector<wchar_t> buf(size / 2 + 1);
- if (!EvtRender(NULL, hEvent, EvtRenderEventXml, size, &buf[0], &used, &propertyCount)) {
- LOG_LAST_ERROR(EvtRender);
- return false;
- }
-
- logger_->log_debug("Event rendered with size %" PRIu32 ". Performing doc traversing...", size);
-
- std::string xml = wel::to_string(&buf[0]);
+ std::string xml = wel::to_string(buf.get());
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_string(xml.c_str());
diff --git a/extensions/windows-event-log/wel/WindowsEventLog.cpp b/extensions/windows-event-log/wel/WindowsEventLog.cpp
index e0a8930..8d1334b 100644
--- a/extensions/windows-event-log/wel/WindowsEventLog.cpp
+++ b/extensions/windows-event-log/wel/WindowsEventLog.cpp
@@ -31,10 +31,12 @@
void WindowsEventLogMetadataImpl::renderMetadata() {
DWORD status = ERROR_SUCCESS;
- DWORD dwBufferSize = 0;
+ EVT_VARIANT stackBuffer[4096];
+ DWORD dwBufferSize = sizeof(stackBuffer);
+ using Deleter = utils::StackAwareDeleter<EVT_VARIANT, utils::FreeDeleter>;
+ std::unique_ptr<EVT_VARIANT, Deleter> rendered_values{ stackBuffer, Deleter{stackBuffer} };
DWORD dwBufferUsed = 0;
DWORD dwPropertyCount = 0;
- std::unique_ptr< EVT_VARIANT, utils::FreeDeleter> rendered_values;
auto context = EvtCreateRenderContext(0, NULL, EvtRenderContextSystem);
if (context == NULL) {
@@ -43,13 +45,14 @@
const auto contextGuard = gsl::finally([&context](){
EvtClose(context);
});
- if (!EvtRender(context, event_ptr_, EvtRenderEventValues, dwBufferSize, nullptr, &dwBufferUsed, &dwPropertyCount)) {
+ if (!EvtRender(context, event_ptr_, EvtRenderEventValues, dwBufferSize, rendered_values.get(), &dwBufferUsed, &dwPropertyCount)) {
if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError())) {
dwBufferSize = dwBufferUsed;
- rendered_values = std::unique_ptr<EVT_VARIANT, utils::FreeDeleter>((PEVT_VARIANT)(malloc(dwBufferSize)));
- if (rendered_values) {
- EvtRender(context, event_ptr_, EvtRenderEventValues, dwBufferSize, rendered_values.get(), &dwBufferUsed, &dwPropertyCount);
+ rendered_values.reset((PEVT_VARIANT)(malloc(dwBufferSize)));
+ if (!rendered_values) {
+ return;
}
+ EvtRender(context, event_ptr_, EvtRenderEventValues, dwBufferSize, rendered_values.get(), &dwBufferUsed, &dwPropertyCount);
}
else {
return;
@@ -118,9 +121,11 @@
}
std::string WindowsEventLogMetadataImpl::getEventData(EVT_FORMAT_MESSAGE_FLAGS flags) const {
- LPWSTR string_buffer = NULL;
- DWORD string_buffer_size = 0;
- DWORD string_buffer_used = 0;
+ WCHAR stack_buffer[4096];
+ DWORD num_chars_in_buffer = sizeof(stack_buffer) / sizeof(stack_buffer[0]);
+ using Deleter = utils::StackAwareDeleter<WCHAR, utils::FreeDeleter>;
+ std::unique_ptr<WCHAR, Deleter> buffer{ stack_buffer, Deleter{stack_buffer} };
+ DWORD num_chars_used = 0;
DWORD result = 0;
std::string event_data;
@@ -129,56 +134,56 @@
return event_data;
}
- if (!EvtFormatMessage(metadata_ptr_, event_ptr_, 0, 0, NULL, flags, string_buffer_size, string_buffer, &string_buffer_used)) {
+
+ if (!EvtFormatMessage(metadata_ptr_, event_ptr_, 0, 0, NULL, flags, num_chars_in_buffer, buffer.get(), &num_chars_used)) {
result = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == result) {
- string_buffer_size = string_buffer_used;
+ num_chars_in_buffer = num_chars_used;
- string_buffer = (LPWSTR) malloc(string_buffer_size * sizeof(WCHAR));
-
- if (string_buffer) {
-
- if ((EvtFormatMessageKeyword == flags))
- string_buffer[string_buffer_size - 1] = L'\0';
-
- EvtFormatMessage(metadata_ptr_, event_ptr_, 0, 0, NULL, flags, string_buffer_size, string_buffer, &string_buffer_used);
- if ((EvtFormatMessageKeyword == flags))
- string_buffer[string_buffer_used - 1] = L'\0';
- std::wstring str(string_buffer);
- event_data = std::string(str.begin(), str.end());
- free(string_buffer);
+ buffer.reset((LPWSTR) malloc(num_chars_in_buffer * sizeof(WCHAR)));
+ if (!buffer) {
+ return event_data;
}
+
+ EvtFormatMessage(metadata_ptr_, event_ptr_, 0, 0, NULL, flags, num_chars_in_buffer, buffer.get(), &num_chars_used);
}
}
+ if (EvtFormatMessageKeyword == flags) {
+ buffer.get()[num_chars_used - 1] = L'\0';
+ }
+ std::wstring str(buffer.get());
+ event_data = std::string(str.begin(), str.end());
return event_data;
}
std::string WindowsEventLogHandler::getEventMessage(EVT_HANDLE eventHandle) const {
std::string returnValue;
- std::unique_ptr<WCHAR, utils::FreeDeleter> pBuffer;
- DWORD dwBufferSize = 0;
- DWORD dwBufferUsed = 0;
+ WCHAR stack_buffer[4096];
+ DWORD num_chars_in_buffer = sizeof(stack_buffer) / sizeof(stack_buffer[0]);
+ using Deleter = utils::StackAwareDeleter<WCHAR, utils::FreeDeleter>;
+ std::unique_ptr<WCHAR, Deleter> buffer{ stack_buffer, Deleter{stack_buffer} };
+ DWORD num_chars_used = 0;
DWORD status = 0;
- EvtFormatMessage(metadata_provider_, eventHandle, 0, 0, NULL, EvtFormatMessageEvent, dwBufferSize, pBuffer.get(), &dwBufferUsed);
- if (dwBufferUsed == 0) {
+ EvtFormatMessage(metadata_provider_, eventHandle, 0, 0, NULL, EvtFormatMessageEvent, num_chars_in_buffer, buffer.get(), &num_chars_used);
+ if (num_chars_used == 0) {
return returnValue;
}
// we need to get the size of the buffer
status = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == status) {
- dwBufferSize = dwBufferUsed;
+ num_chars_in_buffer = num_chars_used;
/* All C++ examples use malloc and even HeapAlloc in some cases. To avoid any problems ( with EvtFormatMessage calling
free for example ) we will continue to use malloc and use a custom deleter with unique_ptr.
'*/
- pBuffer = std::unique_ptr<WCHAR, utils::FreeDeleter>((LPWSTR)malloc(dwBufferSize * sizeof(WCHAR)));
- if (!pBuffer) {
+ buffer.reset((LPWSTR)malloc(num_chars_in_buffer * sizeof(WCHAR)));
+ if (!buffer) {
return returnValue;
}
- EvtFormatMessage(metadata_provider_, eventHandle, 0, 0, NULL, EvtFormatMessageEvent, dwBufferSize, pBuffer.get(), &dwBufferUsed);
+ EvtFormatMessage(metadata_provider_, eventHandle, 0, 0, NULL, EvtFormatMessageEvent, num_chars_in_buffer, buffer.get(), &num_chars_used);
}
if (ERROR_EVT_MESSAGE_NOT_FOUND == status || ERROR_EVT_MESSAGE_ID_NOT_FOUND == status) {
@@ -186,7 +191,7 @@
}
// convert wstring to std::string
- return to_string(pBuffer.get());
+ return to_string(buffer.get());
}
void WindowsEventLogHeader::setDelimiter(const std::string &delim) {
diff --git a/libminifi/include/utils/Deleters.h b/libminifi/include/utils/Deleters.h
index e694475..9ae11e2 100644
--- a/libminifi/include/utils/Deleters.h
+++ b/libminifi/include/utils/Deleters.h
@@ -26,6 +26,7 @@
#include <netdb.h>
#include <ifaddrs.h>
#endif /* WIN32 */
+#include <utility>
namespace org {
namespace apache {
@@ -40,6 +41,31 @@
}
};
+/**
+ * Allows smart pointers to store a pointer both
+ * to the stack and the heap while ensuring selective
+ * destruction of only heap allocated objects.
+ * @tparam T type of the object
+ * @tparam D type of the deleter used for heap instances
+ */
+template<typename T, typename D>
+struct StackAwareDeleter {
+ template<typename ...Args>
+ explicit StackAwareDeleter(T* stack_instance, Args&& ...args)
+ : stack_instance_{stack_instance},
+ impl_{std::forward<Args>(args)...} {}
+
+ void operator()(T* const ptr) const noexcept(noexcept(impl_(ptr))) {
+ if (ptr != stack_instance_) {
+ impl_(ptr);
+ }
+ }
+
+ private:
+ T* stack_instance_;
+ D impl_;
+};
+
struct addrinfo_deleter {
void operator()(addrinfo* const p) const noexcept {
freeaddrinfo(p);