]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
[Core, mod_commands] Implement memory allocation statistics by adding the "memory... pythonfix
authorAndrey Volk <andywolk@gmail.com>
Sat, 17 Jul 2021 19:34:43 +0000 (22:34 +0300)
committerAndrey Volk <andywolk@gmail.com>
Wed, 20 Oct 2021 14:53:18 +0000 (17:53 +0300)
src/include/switch_utils.h
src/mod/applications/mod_commands/mod_commands.c
src/switch_utils.c

index 9877829824abaa5f6e63438a7b051d98ed77d039..649ad926b36009052b6b2f1af95ea8a3dc0bb6c7 100644 (file)
@@ -1488,6 +1488,7 @@ SWITCH_DECLARE(switch_status_t) switch_digest(const char *digest_name, unsigned
 SWITCH_DECLARE(switch_status_t) switch_digest_string(const char *digest_name, char **digest_str, const void *input, switch_size_t inputLen, unsigned int *outputlen);
 
 SWITCH_DECLARE(char *) switch_must_strdup(const char *_s);
+SWITCH_DECLARE(const char *) switch_memory_usage_stream(switch_stream_handle_t *stream);
 
 SWITCH_END_EXTERN_C
 #endif
index ad5bd51ce973a0baf27bd01d060273ce556b3203..aec3097e45ca6864fbf076d7061551170ba6a3ee 100644 (file)
@@ -7485,6 +7485,18 @@ SWITCH_STANDARD_API(json_function)
        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;
@@ -7652,6 +7664,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
        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, "");
 
index 932e9d963c8006fad28115fcdbb2828ad6122ca9..c19a4c6b60e50403bfdcfc130ac5d44e7cddeba4 100644 (file)
@@ -42,6 +42,7 @@
 #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;
@@ -4646,6 +4651,138 @@ SWITCH_DECLARE(char *) switch_must_strdup(const char *_s)
        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