option ( ENABLE_PROFILE "Enable profiling options (developers only)" OFF )
option ( DISABLE_SNORT_PROFILER "Disable snort Profiler (developers only)" OFF )
option ( ENABLE_DEEP_PROFILING "Enable deep profiling of snort functions (developers only)" OFF )
-option ( ENABLE_MEMORY_PROFILER "Enable memory profiler (developers only)" OFF )
+option ( ENABLE_MEMORY_PROFILER "Enable memory profiler" OFF )
option ( ENABLE_RULE_PROFILER "Enable rule keyword profiler (developers only)" OFF )
option ( ENABLE_ADDRESS_SANITIZER "enable address sanitizer support" OFF )
option ( ENABLE_THREAD_SANITIZER "enable thread sanitizer support" OFF )
* double-freeing any memory. Not guaranteed to be
* thread-safe, but it will prevent the simple cases.
*/
- static bool already_exiting = false;
if ( already_exiting )
return;
already_exiting = true;
bool Snort::reloading = false;
bool Snort::privileges_dropped = false;
+bool Snort::already_exiting = false;
bool Snort::is_reloading()
{ return reloading; }
static bool has_dropped_privileges();
SO_PUBLIC static bool is_reloading();
+ inline SO_PUBLIC static bool is_exiting() { return already_exiting; }
private:
static void init(int, char**);
static bool initializing;
static bool reloading;
static bool privileges_dropped;
+ static bool already_exiting;
};
// RAII-style mechanism for removal and reinstallation of Snort's crash handler
//-------------------------------------------------------------------------
static THREAD_LOCAL uint16_t run_num = 0;
-static THREAD_LOCAL unsigned instance_id = 0;
+THREAD_LOCAL unsigned instance_id = 0;
static THREAD_LOCAL SThreadType thread_type = STHREAD_TYPE_OTHER;
void set_run_num(uint16_t num)
memory_module.cc
memory_module.h
memory_overloads.cc
+ memory_overloads.h
)
set ( ALLOC_SOURCES
- memory_allocator.cc
memory_allocator.h
)
static void deallocate(void*);
};
+inline void* MemoryAllocator::allocate(size_t n)
+{ return malloc(n); }
+
+inline void MemoryAllocator::deallocate(void* p)
+{ free(p); }
+
} // namespace memory
#endif
// memory_overloads.cc author Joel Cornett <jocornet@cisco.com>
+#include "memory_overloads.h"
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <cassert>
-#include <new>
+#include "main/snort.h"
#include "main/thread.h"
#include "profiler/memory_profiler_active_context.h"
#include "catch/snort_catch.h"
#endif
+extern THREAD_LOCAL unsigned instance_id;
+
namespace memory
{
// number of requested bytes
size_t payload_size;
+ uint32_t thread_id;
+ // stat used to keep track of allocation/deallocation
+ MemoryTracker* mp_inspector_stats = nullptr;
// total number of bytes allocated, including Metadata header
size_t total_size() const;
#if defined(REG_TEST) || defined(UNIT_TEST)
sanity(SANITY_CHECK_VALUE),
#endif
- payload_size(n)
+ payload_size(n), thread_id(instance_id),
+ mp_inspector_stats(&mp_active_context.get_default())
{ }
inline size_t Metadata::calculate_total_size(size_t n)
Metadata* Metadata::extract(void* p)
{
- assert(p);
-
auto meta = static_cast<Metadata*>(p) - 1;
#if defined(REG_TEST) || defined(UNIT_TEST)
// the meat
// -----------------------------------------------------------------------------
+#ifdef REG_TEST
class ReentryContext
{
public:
const bool already_entered;
bool& flag;
};
+#endif
template<typename Allocator = MemoryAllocator>
struct Interface
template<typename Allocator>
void* Interface<Allocator>::allocate(size_t n)
{
+#ifdef REG_TEST
// prevent allocation reentry
ReentryContext reentry_context(in_allocation_call);
assert(!reentry_context.is_reentry());
-
+#endif
auto meta = Metadata::create<Allocator>(n);
if ( !meta )
assert(meta);
#ifdef ENABLE_MEMORY_PROFILER
- mp_active_context.update_deallocs(meta->total_size());
+ if (!snort::Snort::is_exiting())
+ {
+ if (meta->mp_inspector_stats and meta->thread_id == instance_id)
+ meta->mp_inspector_stats->update_deallocs(meta->total_size());
+ else
+ mp_active_context.update_deallocs(meta->total_size());
+ }
#endif
Allocator::deallocate(meta);
void operator delete[](void* p, size_t) noexcept
{ ::operator delete[](p); }
+
+void operator delete[](void* p, size_t, const std::nothrow_t&) noexcept
+{ ::operator delete[](p); }
+
+void operator delete(void* p, size_t, const std::nothrow_t&) noexcept
+{ ::operator delete(p); }
+
#endif
// -----------------------------------------------------------------------------
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2023 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// memory_overloads.h author Sunirmal Mukherjee<sunimukh@cisco.com>
+
+#ifndef MEMORY_OVERLOADS_H
+#define MEMORY_OVERLOADS_H
+
+#include <new>
+
+// -----------------------------------------------------------------------------
+// new /delete replacements
+// -----------------------------------------------------------------------------
+
+// these don't have to be visible to operate as replacements
+
+
+void* operator new(std::size_t);
+void* operator new[](std::size_t);
+void* operator new(std::size_t, const std::nothrow_t&) noexcept;
+void* operator new[](std::size_t, const std::nothrow_t&) noexcept;
+void operator delete(void*) noexcept;
+void operator delete[](void*) noexcept;
+void operator delete(void*, const std::nothrow_t&) noexcept;
+void operator delete[](void*, const std::nothrow_t&) noexcept;
+void operator delete(void*, std::size_t) noexcept;
+void operator delete[](void*, std::size_t) noexcept;
+void operator delete[](void*, std::size_t, const std::nothrow_t&) noexcept;
+void operator delete(void*, std::size_t, const std::nothrow_t&) noexcept;
+
+#endif
#include "log/messages.h"
#include "main/thread.h"
+#include "profiler/profiler.h"
#include "time/periodic.h"
#include "trace/trace_api.h"
void Periodic::register_handler(PeriodicHook, void*, uint16_t, uint32_t) { }
+void Profiler::register_module(const char*, const char*, snort::Module*) { }
+
//--------------------------------------------------------------------------
// mocks
//--------------------------------------------------------------------------
{
if (ready_to_process(p))
{
+#ifdef ENABLE_MEMORY_PROFILER
+ Profiler::show_runtime_memory_stats();
+#endif
for (unsigned i = 0; i < trackers->size(); i++)
{
(*trackers)[i]->process(false);
// -----------------------------------------------------------------------------
THREAD_LOCAL MemoryActiveContext mp_active_context;
-static CombinedMemoryStats s_fallthrough_stats;
+static MemoryStats s_fallthrough_stats;
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// get thread local default stats
-const CombinedMemoryStats& MemoryProfiler::get_fallthrough_stats()
+const MemoryStats& MemoryProfiler::get_fallthrough_stats()
{ return s_fallthrough_stats; }
// thread local call
#ifndef MEMORY_CONTEXT_H
#define MEMORY_CONTEXT_H
-struct CombinedMemoryStats;
+struct MemoryStats;
class MemoryProfiler
{
public:
// global accumulated stats
- static const CombinedMemoryStats& get_fallthrough_stats();
+ static const MemoryStats& get_fallthrough_stats();
// thread local call
static void consolidate_fallthrough_stats();
return *this;
}
-struct CombinedMemoryStats
-{
- MemoryStats startup;
- MemoryStats runtime;
-
- void update_allocs(size_t);
- void update_deallocs(size_t);
-
- void reset();
-
- operator bool() const
- { return startup || runtime; }
-
- bool operator==(const CombinedMemoryStats&) const;
- bool operator!=(const CombinedMemoryStats& rhs) const
- { return !(*this == rhs); }
-
- CombinedMemoryStats& operator+=(const CombinedMemoryStats&);
-};
-
-inline void CombinedMemoryStats::update_allocs(size_t n)
-{
- if ( snort::is_packet_thread() )
- runtime.update_allocs(n);
- else
- startup.update_allocs(n);
-}
-
-inline void CombinedMemoryStats::update_deallocs(size_t n)
-{
- if ( snort::is_packet_thread() )
- runtime.update_deallocs(n);
- else
- startup.update_deallocs(n);
-}
-
-inline void CombinedMemoryStats::reset()
-{
- startup.reset();
- runtime.reset();
-}
-
-inline bool CombinedMemoryStats::operator==(const CombinedMemoryStats& rhs) const
-{ return startup == rhs.startup && runtime == rhs.runtime; }
-
-inline CombinedMemoryStats& CombinedMemoryStats::operator+=(const CombinedMemoryStats& rhs)
-{
- startup += rhs.startup;
- runtime += rhs.runtime;
-
- return *this;
-}
-
struct MemoryTracker
{
- CombinedMemoryStats stats;
+ MemoryStats stats;
void reset()
{ stats.reset(); }
{ stats.update_deallocs(n); }
constexpr MemoryTracker() = default;
- constexpr MemoryTracker(const CombinedMemoryStats &stats) : stats(stats) { }
+ constexpr MemoryTracker(const MemoryStats &stats) : stats(stats) { }
};
#endif
{ return !(*this == rhs); }
View(const ProfilerNode& node, View* parent = nullptr) :
- name(node.name), stats(new MemoryStats(node.get_stats().memory.stats.runtime))
+ name(node.name), stats(new MemoryStats(node.get_stats().memory.stats))
{
if ( parent )
{
};
static bool include_fn(const ProfilerNode& node)
-{ return node.get_stats().memory.stats.runtime; }
+{ return node.get_stats().memory.stats; }
static void print_fn(StatsTable& t, const View& v)
{
ProfileStats the_stats;
ProfilerNode node("foo");
- the_stats.memory.stats.runtime = { 1, 2, 100, 4 };
+ the_stats.memory.stats = { 1, 2, 100, 4 };
node.set_stats(the_stats);
SECTION( "no parent" )
SECTION( "ctor sets members" )
{
- CHECK( *view.stats == the_stats.memory.stats.runtime );
+ CHECK( *view.stats == the_stats.memory.stats );
CHECK( view.caller_stats == nullptr );
}
bool show = false;
unsigned count = 0;
int max_depth = -1;
+ uint64_t dump_file_size = 0;
};
namespace snort
show_rule_profiler_stats(config->rule);
}
+void Profiler::show_runtime_memory_stats()
+{
+ s_profiler_nodes.print_runtime_memory_stats();
+}
+
#ifdef UNIT_TEST
TEST_CASE( "profile stats", "[profiler]" )
SECTION( "il" )
{
TimeProfilerStats time_stats = { 12_ticks, 2 };
- MemoryTracker memory_stats =
- {{
- { 1, 2, 3, 4 },
- { 5, 6, 7, 8 }
- }};
+ MemoryTracker memory_stats = {{ 1, 2, 3, 4 }};
ProfileStats stats(time_stats, memory_stats);
{
ProfileStats stats {
{ 1_ticks, 2 },
- {{
- { 1, 2, 3, 4 },
- { 5, 6, 7, 8 },
- }}
+ {{ 1, 2, 3, 4 }}
};
SECTION( "reset" )
ProfileStats other_stats {
{ 12_ticks, 12 },
- {{
- { 5, 6, 7, 8 },
- { 9, 10, 11, 12 }
- }}
+ {{ 5, 6, 7, 8 }}
};
SECTION( "==/!=" )
{
stats += other_stats;
CHECK( stats.time == TimeProfilerStats(13_ticks, 14) );
- CombinedMemoryStats memory_result = {
- { 6, 8, 10, 12 },
- { 14, 16, 18, 20 }
- };
+ MemoryStats memory_result = { 6, 8, 10, 12 };
CHECK( stats.memory.stats == memory_result );
}
static void prepare_stats();
static void show_stats();
static ProfilerNodeMap& get_profiler_nodes();
+ SO_PUBLIC static void show_runtime_memory_stats();
};
extern THREAD_LOCAL snort::ProfileStats totalPerfStats;
{ "max_depth", Parameter::PT_INT, "-1:255", "-1",
"limit depth to max_depth (-1 = no limit)" },
+ { "dump_file_size", Parameter::PT_INT, "4096:max53", "1073741824",
+ "files will be rolled over if they exceed this size" },
+
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
static bool s_profiler_module_set_max_depth(RuleProfilerConfig&, Value&)
{ return false; }
+template<typename T>
+static bool s_profiler_module_set_dump_file_size(T&, Value&)
+{ return false; }
+
+// cppcheck-suppress constParameter
+static bool s_profiler_module_set_dump_file_size(MemoryProfilerConfig& config, Value& v)
+{ config.dump_file_size = v.get_int64(); return true; }
+
template<typename T>
static bool s_profiler_module_set(T& config, Value& v)
{
else if ( v.is("max_depth") )
return s_profiler_module_set_max_depth(config, v);
+ else if ( v.is("dump_file_size") )
+ return s_profiler_module_set_dump_file_size(config, v);
+
else
return false;
#include "profiler_nodes.h"
#include <cassert>
+#include <fstream>
#include <mutex>
+#include <sys/stat.h>
+#include <climits>
+#include <unistd.h>
#include "framework/module.h"
+#include "main/snort_config.h"
+#include "memory_profiler_active_context.h"
#include "profiler_defs.h"
+#include "log/messages.h"
+#include "time/packet_time.h"
+#include "utils/util.h"
+#include "utils/util_cstring.h"
#ifdef UNIT_TEST
#include "catch/snort_catch.h"
using namespace snort;
+static std::string tracker_fname = "mem_profile_stats.csv";
+static THREAD_LOCAL FILE* tracker_fd = nullptr;
+
// -----------------------------------------------------------------------------
// types
// -----------------------------------------------------------------------------
}
}
+void ProfilerNodeMap::write_header()
+{
+ fprintf(tracker_fd, "#timestamp,");
+
+ for ( auto it = nodes.begin(); it != nodes.end(); ++it )
+ {
+ const char* ins_name = it->second.name.c_str();
+ fprintf(tracker_fd, "%s.bytes_allocated,%s.bytes_deallocated,"
+ "%s.allocation_count,%s.deallocation_count,", ins_name,
+ ins_name, ins_name, ins_name);
+ }
+
+ fprintf(tracker_fd, "global.bytes_allocated,global.bytes_deallocated,"
+ "global.allocation_count,global.deallocation_count\n");
+
+ fflush(tracker_fd);
+}
+
+static void inline write_memtracking_info(const MemoryStats& stats, FILE* fd)
+{
+ fprintf(fd, "%" PRIu64 ",", (uint64_t)(stats.allocated));
+ fprintf(fd, "%" PRIu64 ",", (uint64_t)(stats.deallocated));
+ fprintf(fd, "%" PRIu64 ",", (uint64_t)(stats.allocs));
+ fprintf(fd, "%" PRIu64 ",", (uint64_t)(stats.deallocs));
+}
+
+void ProfilerNode::get_local_memory_stats(FILE* fd)
+{
+ if (is_set())
+ {
+ const auto* local_stats = (*getter)();
+
+ if (!local_stats)
+ return;
+
+ write_memtracking_info(local_stats->memory.stats, fd);
+ }
+}
+
+bool ProfilerNodeMap::open(std::string& fname, uint64_t max_file_size, bool append)
+{
+ if (fname.length())
+ {
+ struct stat pt;
+ mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+ const char* file_name = fname.c_str();
+ bool existed = false;
+
+ tracker_fd = fopen(file_name, "r");
+ if (tracker_fd)
+ {
+ // Check file before change permission
+ if (fstat(fileno(tracker_fd), &pt) == 0)
+
+ {
+ existed = true;
+
+ // Only change permission for file owned by root
+ if ((0 == pt.st_uid) || (0 == pt.st_gid))
+ {
+ if (fchmod(fileno(tracker_fd), mode) != 0)
+ {
+ WarningMessage("Profiler: Unable to change mode of "
+ "stats file '%s' to mode:%u: %s.\n",
+ file_name, mode, get_error(errno));
+ }
+
+ const SnortConfig* sc = SnortConfig::get_conf();
+
+ if (fchown(fileno(tracker_fd), sc->get_uid(), sc->get_gid()) != 0)
+ {
+ WarningMessage("Profiler: Unable to change permissions of "
+ "stats file '%s' to user:%d and group:%d: %s.\n",
+ file_name, sc->get_uid(), sc->get_gid(), get_error(errno));
+ }
+ }
+ }
+
+ fclose(tracker_fd);
+ tracker_fd = nullptr;
+ }
+
+ // This file needs to be readable by everyone
+ mode_t old_umask = umask(022);
+ // Append to the existing file if just starting up, otherwise we've
+ // rotated so start a new one.
+ tracker_fd = fopen(file_name, append ? "a" : "w");
+ umask(old_umask);
+
+ if (!tracker_fd)
+ {
+ ErrorMessage("Profiler: Cannot open stats file '%s'.\n", file_name);
+ return false;
+ }
+
+ // FIXIT-L refactor rotation so it doesn't require an open file handle
+ if (existed and append)
+ return rotate(fname, max_file_size);
+ }
+
+ return true;
+}
+
+// FIXIT-M combine with fileRotate
+// FIXIT-M refactor file naming foo to use std::string
+static bool rotate_file(const char* old_file, FILE* old_fd,
+ uint32_t max_file_size)
+{
+ time_t ts;
+ char rotate_file[PATH_MAX];
+ struct stat fstats;
+ FILE* rotate_fh;
+
+ if (!old_file)
+ return false;
+
+ if (!old_fd)
+ {
+ ErrorMessage("Profiler: Memtracker stats file \"%s\" "
+ "isn't open.\n", old_file);
+ return false;
+ }
+
+ // Mostly file mode is "a", so can't use rewind() or fseek(). Had to close and reopen.
+ fclose(old_fd);
+ old_fd = fopen(old_file, "r");
+
+ // Fetching the first timestamp from the file and renaming it by appending the time
+ int line = 0;
+ int holder;
+
+ while((holder=fgetc(old_fd)) != EOF)
+ {
+ if (holder == '\n')
+ line++;
+ if (line == 1)
+ break;
+ }
+
+ // Use the current time if 2nd line is not there
+ if (holder == EOF)
+ ts = time(nullptr);
+ else
+ {
+ char line_str[15];
+ if (!fgets(line_str, 15, old_fd))
+ ts = time(nullptr);
+ else
+ {
+ char* p = strtok(line_str, ",");
+ ts = atoll(p);
+ }
+ }
+
+ fclose(old_fd);
+ old_fd = nullptr;
+
+ // Create rotate file name based on path, optional prefix and date
+ // Need to be mindful that we get 64-bit times on OSX
+ SnortSnprintf(rotate_file, PATH_MAX, "%s_" STDu64, old_file, (uint64_t)ts);
+
+ // If the rotate file doesn't exist, just rename the old one to the new one
+ rotate_fh = fopen(rotate_file, "r");
+ if (rotate_fh == NULL)
+ {
+ if (rename(old_file, rotate_file) != 0)
+ {
+ ErrorMessage("Profiler: Could not rename Memtracker stats "
+ "file from \"%s\" to \"%s\": %s.\n",
+ old_file, rotate_file, get_error(errno));
+ }
+ }
+ else // Otherwise, if it does exist, append data from current stats file to it
+ {
+ char read_buf[4096];
+ size_t num_read, num_wrote;
+ int rotate_index = 0;
+ char rotate_file_with_index[PATH_MAX];
+
+ // This file needs to be readable by everyone
+ mode_t old_umask = umask(022);
+
+ fclose(rotate_fh);
+ rotate_fh = nullptr;
+
+ do
+ {
+ do
+ {
+ rotate_index++;
+
+ // Check to see if there are any files already rotated and indexed
+ SnortSnprintf(rotate_file_with_index, PATH_MAX, "%s.%02d",
+ rotate_file, rotate_index);
+ }
+ while (stat(rotate_file_with_index, &fstats) == 0);
+
+ // Subtract one to append to last existing file
+ rotate_index--;
+
+ if (rotate_index == 0)
+ {
+ rotate_file_with_index[0] = 0;
+ rotate_fh = fopen(rotate_file, "a");
+ }
+ else
+ {
+ SnortSnprintf(rotate_file_with_index, PATH_MAX, "%s.%02d",
+ rotate_file, rotate_index);
+ rotate_fh = fopen(rotate_file_with_index, "a");
+ }
+
+ if (!rotate_fh)
+ {
+ ErrorMessage("Profiler: Could not open Memtracker stats "
+ "archive file \"%s\" for appending: %s.\n",
+ *rotate_file_with_index ? rotate_file_with_index : rotate_file,
+ get_error(errno));
+ break;
+ }
+
+ old_fd = fopen(old_file, "r");
+ if (!old_fd)
+ {
+ ErrorMessage("Profiler: Could not open Memtracker stats file "
+ "\"%s\" for reading to copy to archive \"%s\": %s.\n",
+ old_file, (*rotate_file_with_index ? rotate_file_with_index :
+ rotate_file), get_error(errno));
+ break;
+ }
+
+ while (!feof(old_fd))
+ {
+ // This includes the newline from the file.
+ if (!fgets(read_buf, sizeof(read_buf), old_fd))
+ {
+ if (feof(old_fd))
+ break;
+
+ if (ferror(old_fd))
+ {
+ // A read error occurred
+ ErrorMessage("Profiler: Error reading Memtracker stats "
+ "file \"%s\": %s.\n", old_file, get_error(errno));
+ break;
+ }
+ }
+
+ num_read = strlen(read_buf);
+
+ if (num_read > 0)
+ {
+ int rotate_fd = fileno(rotate_fh);
+
+ if (fstat(rotate_fd, &fstats) != 0)
+ {
+ ErrorMessage("Profiler: Error getting file "
+ "information for \"%s\": %s.\n",
+ *rotate_file_with_index ? rotate_file_with_index : rotate_file,
+ get_error(errno));
+ break;
+ }
+
+ if (((uint32_t)fstats.st_size + num_read) > max_file_size)
+ {
+ fclose(rotate_fh);
+
+ rotate_index++;
+
+ // Create new file same as before but with an index added to the end
+ SnortSnprintf(rotate_file_with_index, PATH_MAX, "%s.%02d",
+ rotate_file, rotate_index);
+
+ rotate_fh = fopen(rotate_file_with_index, "a");
+ if (!rotate_fh)
+ {
+ ErrorMessage("Profiler: Could not open Memtracker "
+ "stats archive file \"%s\" for writing: %s.\n",
+ rotate_file_with_index, get_error(errno));
+ break;
+ }
+ }
+
+ num_wrote = fprintf(rotate_fh, "%s", read_buf);
+ if ((num_wrote != num_read) && ferror(rotate_fh))
+ {
+ // A bad write occurred
+ ErrorMessage("Profiler: Error writing to Memtracker "
+ "stats archive file \"%s\": %s.\n", rotate_file, get_error(errno));
+ break;
+ }
+
+ fflush(rotate_fh);
+ }
+ }
+ }
+ while (false);
+
+ if (rotate_fh)
+ fclose(rotate_fh);
+
+ if (old_fd)
+ fclose(old_fd);
+
+ umask(old_umask);
+ }
+
+ return true;
+}
+
+bool ProfilerNodeMap::rotate(std::string& fname, uint64_t max_file_size)
+{
+ if (tracker_fd)
+ {
+ if (!rotate_file(fname.c_str(), tracker_fd, max_file_size))
+ return false;
+
+ return open(fname, max_file_size, false);
+ }
+
+ return false;
+}
+
+static inline bool check_file_size(FILE* fh, uint64_t max_file_size)
+{
+ int fd;
+ struct stat fstats;
+
+ if (!fh)
+ return false;
+
+ fd = fileno(fh);
+ if ((fstat(fd, &fstats) == 0)
+ and ((uint64_t)fstats.st_size >= max_file_size))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+inline void ProfilerNodeMap::create_new_file(std::string& fname, uint64_t max_file_size)
+{
+ open(fname, max_file_size, true);
+ write_header();
+}
+
+void ProfilerNodeMap::auto_rotate(std::string& fname, uint64_t max_file_size)
+{
+ const char* file_name = fname.c_str();
+
+ if (tracker_fd)
+ {
+ // If file is deleted, will close the existing fd and reopen the file
+ if (access(file_name, F_OK) != 0)
+ {
+ fclose(tracker_fd);
+ tracker_fd = nullptr;
+ create_new_file(fname, max_file_size);
+ }
+ // If file size exceeds max size, will rotate the file and open a new one.
+ else if (check_file_size(tracker_fd, max_file_size))
+ {
+ rotate(fname, max_file_size);
+ write_header();
+ }
+ }
+ else
+ {
+ // If after restart file exists, will append to the existing file.
+ FILE* fd = fopen(file_name, "r");
+ if (fd)
+ {
+ if (check_file_size(fd, max_file_size))
+ {
+ fclose(fd);
+ tracker_fd = fopen(file_name, "a");
+ return;
+ }
+
+ fclose(fd);
+ }
+
+ create_new_file(fname, max_file_size);
+ }
+
+}
+
+void ProfilerNodeMap::print_runtime_memory_stats()
+{
+ const auto* config = SnortConfig::get_conf()->get_profiler();
+ if (!config->memory.show)
+ return;
+
+ std::string fname;
+ get_instance_file(fname, tracker_fname.c_str());
+
+ auto_rotate(fname, config->memory.dump_file_size);
+
+ timeval cur_time;
+ packet_gettimeofday(&cur_time);
+
+ fprintf(tracker_fd, "%" PRIu64 ",", (uint64_t)cur_time.tv_sec);
+
+ for ( auto it = nodes.begin(); it != nodes.end(); ++it )
+ it->second.get_local_memory_stats(tracker_fd);
+
+ write_memtracking_info(mp_active_context.get_fallback().stats, tracker_fd);
+
+ fputs("\n", tracker_fd);
+ fflush(tracker_fd);
+}
+
void ProfilerNodeMap::register_node(const std::string &n, const char* pn, Module* m)
{ setup_node(get_node(n), get_node(pn ? pn : ROOT_NODE), m); }
const std::string name;
+ void get_local_memory_stats(FILE*);
+
private:
std::vector<ProfilerNode*> children;
std::shared_ptr<GetProfileFunctor> getter;
void clear_flex();
void reset_nodes(snort::ProfilerType = snort::PROFILER_TYPE_BOTH);
+ void print_runtime_memory_stats();
+
+ inline void create_new_file(std::string&, uint64_t);
+ void auto_rotate(std::string&, uint64_t);
+ bool rotate(std::string&, uint64_t);
+ bool open(std::string&, uint64_t, bool);
+ void write_header();
+
const ProfilerNode& get_root();
private:
static int64_t get_decimal_num(enum NumericValue& is_numeric, const uint8_t* start, int32_t length)
{
+ if (!length)
+ {
+ is_numeric = NV_FALSE;
+ return -1;
+ }
+
int64_t total = 0;
int32_t k = 0;
do