return SWITCH_STATUS_SUCCESS;
}
+SWITCH_STANDARD_API(memory_function)
+{
+ const char *err;
+ if (!(err = switch_memory_usage_stream(stream))) {
+ stream->write_function(stream, "+OK\n");
+ } else {
+ stream->write_function(stream, "-ERR %s\n", err);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
{
switch_api_interface_t *commands_api_interface;
SWITCH_ADD_API(commands_api_interface, "file_exists", "Check if a file exists on server", file_exists_function, "<file>");
SWITCH_ADD_API(commands_api_interface, "getcputime", "Gets CPU time in milliseconds (user,kernel)", getcputime_function, GETCPUTIME_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "json", "JSON API", json_function, "JSON");
+ SWITCH_ADD_API(commands_api_interface, "memory", "Memory usage statistics", memory_function, "memory");
SWITCH_ADD_JSON_API(json_api_interface, "mediaStats", "JSON Media Stats", json_stats_function, "");
#include <sys/types.h>
#include <unistd.h>
#else
+#include <intsafe.h> /* SIZETMult() */
/* process.h is required for _getpid() */
#include <process.h>
#endif
#include <openssl/evp.h>
#endif
+#ifdef __GLIBC__
+#include <malloc.h> /* mallinfo() */
+#endif
+
struct switch_network_node {
ip_t ip;
ip_t mask;
return s;
}
+SWITCH_DECLARE(const char *) switch_memory_usage_stream(switch_stream_handle_t *stream)
+{
+ const char *status = NULL;
+#ifdef __GLIBC__
+/*
+ * The mallinfo2() function was added in glibc 2.33.
+ * https://man7.org/linux/man-pages/man3/mallinfo.3.html
+ */
+#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 33)
+ struct mallinfo2 mi;
+
+ mi = mallinfo2();
+
+ stream->write_function(stream, "Total non-mmapped bytes (arena): %" SWITCH_SIZE_T_FMT "\n", mi.arena);
+ stream->write_function(stream, "# of free chunks (ordblks): %" SWITCH_SIZE_T_FMT "\n", mi.ordblks);
+ stream->write_function(stream, "# of free fastbin blocks (smblks): %" SWITCH_SIZE_T_FMT "\n", mi.smblks);
+ stream->write_function(stream, "# of mapped regions (hblks): %" SWITCH_SIZE_T_FMT "\n", mi.hblks);
+ stream->write_function(stream, "Bytes in mapped regions (hblkhd): %" SWITCH_SIZE_T_FMT "\n", mi.hblkhd);
+ stream->write_function(stream, "Max. total allocated space (usmblks): %" SWITCH_SIZE_T_FMT "\n", mi.usmblks);
+ stream->write_function(stream, "Free bytes held in fastbins (fsmblks): %" SWITCH_SIZE_T_FMT "\n", mi.fsmblks);
+ stream->write_function(stream, "Total allocated space (uordblks): %" SWITCH_SIZE_T_FMT "\n", mi.uordblks);
+ stream->write_function(stream, "Total free space (fordblks): %" SWITCH_SIZE_T_FMT "\n", mi.fordblks);
+ stream->write_function(stream, "Topmost releasable block (keepcost): %" SWITCH_SIZE_T_FMT "\n", mi.keepcost);
+#else
+ struct mallinfo mi;
+
+ mi = mallinfo();
+
+ stream->write_function(stream, "Total non-mmapped bytes (arena): %u\n", mi.arena);
+ stream->write_function(stream, "# of free chunks (ordblks): %u\n", mi.ordblks);
+ stream->write_function(stream, "# of free fastbin blocks (smblks): %u\n", mi.smblks);
+ stream->write_function(stream, "# of mapped regions (hblks): %u\n", mi.hblks);
+ stream->write_function(stream, "Bytes in mapped regions (hblkhd): %u\n", mi.hblkhd);
+ stream->write_function(stream, "Max. total allocated space (usmblks): %u\n", mi.usmblks);
+ stream->write_function(stream, "Free bytes held in fastbins (fsmblks): %u\n", mi.fsmblks);
+ stream->write_function(stream, "Total allocated space (uordblks): %u\n", mi.uordblks);
+ stream->write_function(stream, "Total free space (fordblks): %u\n", mi.fordblks);
+ stream->write_function(stream, "Topmost releasable block (keepcost): %u\n", mi.keepcost);
+
+#endif
+
+ switch_goto_status(NULL, done);
+#else
+#ifdef WIN32
+ /* Based on: https://docs.microsoft.com/en-us/windows/win32/memory/enumerating-a-heap and https://docs.microsoft.com/en-us/windows/win32/memory/getting-process-heaps */
+ PHANDLE aHeaps;
+ SIZE_T BytesToAllocate;
+ DWORD HeapsIndex;
+ DWORD HeapsLength;
+ DWORD NumberOfHeaps;
+ HRESULT Result;
+ HANDLE hDefaultProcessHeap;
+ size_t CommittedSizeTotal = 0;
+ size_t UnCommittedSizeTotal = 0;
+ size_t SizeTotal = 0;
+ size_t OverheadTotal = 0;
+
+ NumberOfHeaps = GetProcessHeaps(0, NULL);
+ Result = SIZETMult(NumberOfHeaps, sizeof(*aHeaps), &BytesToAllocate);
+ if (Result != S_OK) {
+ switch_goto_status("SIZETMult failed.", done);
+ }
+
+ hDefaultProcessHeap = GetProcessHeap();
+ if (hDefaultProcessHeap == NULL) {
+ switch_goto_status("Failed to retrieve the default process heap", done);
+ }
+
+ aHeaps = (PHANDLE)HeapAlloc(hDefaultProcessHeap, 0, BytesToAllocate);
+ if (aHeaps == NULL) {
+ switch_goto_status("HeapAlloc failed to allocate space for heaps", done);
+ }
+
+ HeapsLength = NumberOfHeaps;
+ NumberOfHeaps = GetProcessHeaps(HeapsLength, aHeaps);
+
+ if (NumberOfHeaps == 0) {
+ switch_goto_status("Failed to retrieve heaps", cleanup);
+ } else if (NumberOfHeaps > HeapsLength) {
+ /*
+ * Compare the latest number of heaps with the original number of heaps.
+ * If the latest number is larger than the original number, another
+ * component has created a new heap and the buffer is too small.
+ */
+ switch_goto_status("Another component created a heap between calls.", cleanup);
+ }
+
+ stream->write_function(stream, "Process has %d heaps.\n", HeapsLength);
+ for (HeapsIndex = 0; HeapsIndex < HeapsLength; ++HeapsIndex) {
+ PROCESS_HEAP_ENTRY Entry;
+ HANDLE hHeap = aHeaps[HeapsIndex];
+
+ stream->write_function(stream, "Heap %d at address: %#p.\n", HeapsIndex, aHeaps[HeapsIndex]);
+
+ /* Lock the heap to prevent other threads from accessing the heap during enumeration. */
+ if (HeapLock(hHeap) == FALSE) {
+ switch_goto_status("Failed to lock heap.", cleanup);
+ }
+
+ Entry.lpData = NULL;
+ while (HeapWalk(hHeap, &Entry) != FALSE) {
+ if ((Entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) {
+ } else if ((Entry.wFlags & PROCESS_HEAP_REGION) != 0) {
+ CommittedSizeTotal += Entry.Region.dwCommittedSize;
+ UnCommittedSizeTotal += Entry.Region.dwUnCommittedSize;
+ }
+
+ SizeTotal += Entry.cbData;
+ OverheadTotal += Entry.cbOverhead;
+ }
+
+ /* Unlock the heap to allow other threads to access the heap after enumeration has completed. */
+ if (HeapUnlock(hHeap) == FALSE) {
+ abort();
+ }
+ }
+
+ stream->write_function(stream, "Committed bytes: %" SWITCH_SIZE_T_FMT "\n", CommittedSizeTotal);
+ stream->write_function(stream, "Uncommited bytes: %" SWITCH_SIZE_T_FMT "\n", UnCommittedSizeTotal);
+ stream->write_function(stream, "Size: %" SWITCH_SIZE_T_FMT "\n", SizeTotal);
+ stream->write_function(stream, "Overhead: %" SWITCH_SIZE_T_FMT"\n", OverheadTotal);
+
+cleanup:
+ HeapFree(hDefaultProcessHeap, 0, aHeaps);
+#else
+ switch_goto_status("Memory usage statistics is not implemented on the current platform.", done);
+#endif
+#endif
+done:
+ return status;
+}
+
/* For Emacs:
* Local Variables:
* mode:c