[Core, mod_commands] Implement memory allocation statistics by adding the "memory" API command.
This commit is contained in:
parent
30eaf2c736
commit
b8be418fb8
|
@ -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
|
||||
|
|
|
@ -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, "");
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue