[Core, mod_commands] Implement memory allocation statistics by adding the "memory" API command.

This commit is contained in:
Andrey Volk 2021-07-17 22:34:43 +03:00
parent 30eaf2c736
commit b8be418fb8
3 changed files with 151 additions and 0 deletions

View 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

View 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, "");

View 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
@ -55,6 +56,10 @@
#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