/v1/debug/malloc_info: call malloc_info(3) if available

The GNU libc function malloc_info(3) provides memory allocation and usage
statistics of Icinga 2 itself.
This commit is contained in:
Alexander A. Klimov 2024-08-05 13:24:35 +02:00
parent b6b2f72055
commit f3c7ac11e9
6 changed files with 191 additions and 0 deletions

View File

@ -365,6 +365,7 @@ check_function_exists(vfork HAVE_VFORK)
check_function_exists(backtrace_symbols HAVE_BACKTRACE_SYMBOLS)
check_function_exists(pipe2 HAVE_PIPE2)
check_function_exists(nice HAVE_NICE)
check_function_exists(malloc_info HAVE_MALLOC_INFO)
check_library_exists(dl dladdr "dlfcn.h" HAVE_DLADDR)
check_library_exists(execinfo backtrace_symbols "" HAVE_LIBEXECINFO)
check_include_file_cxx(cxxabi.h HAVE_CXXABI_H)

View File

@ -8,6 +8,7 @@
#cmakedefine HAVE_LIBEXECINFO
#cmakedefine HAVE_CXXABI_H
#cmakedefine HAVE_NICE
#cmakedefine HAVE_MALLOC_INFO
#cmakedefine HAVE_EDITLINE
#cmakedefine HAVE_SYSTEMD

View File

@ -288,6 +288,7 @@ Available permissions for specific URL endpoints:
config/query | /v1/config | No | 1
config/modify | /v1/config | No | 512
console | /v1/console | No | 1
debug | /v1/debug | No | 1
events/<type> | /v1/events | No | 1
objects/query/<type> | /v1/objects | Yes | 1
objects/create/<type> | /v1/objects | No | 1
@ -2502,6 +2503,72 @@ curl -k -s -S -i -u root:icinga -H 'Accept: application/json' \
}
```
## Memory Usage Analysis <a id="icinga2-api-memory"></a>
The GNU libc function `malloc_info(3)` provides memory allocation and usage
statistics of Icinga 2 itself. You can call it directly by sending a `GET`
request to the URL endpoint `/v1/debug/malloc_info`.
The [API permission](12-icinga2-api.md#icinga2-api-permissions) `debug` is required.
Example:
```bash
curl -k -s -S -i -u root:icinga https://localhost:5665/v1/debug/malloc_info
```
In contrast to other API endpoints, the response is not JSON,
but the raw XML output from `malloc_info(3)`. See also the
[glibc malloc(3) internals](https://sourceware.org/glibc/wiki/MallocInternals).
```xml
<malloc version="1">
<heap nr="0">
<sizes>
<size from="33" to="48" total="96" count="2"/>
<size from="49" to="64" total="192" count="3"/>
<size from="65" to="80" total="80" count="1"/>
<unsorted from="84817" to="84817" total="84817" count="1"/>
</sizes>
<total type="fast" count="6" size="368"/>
<total type="rest" count="2" size="859217"/>
<system type="current" size="7409664"/>
<system type="max" size="7409664"/>
<aspace type="total" size="7409664"/>
<aspace type="mprotect" size="7409664"/>
</heap>
<!-- ... -->
<heap nr="30">
<sizes>
<size from="17" to="32" total="96" count="3"/>
<size from="33" to="48" total="576" count="12"/>
<size from="49" to="64" total="64" count="1"/>
<size from="97" to="112" total="3584" count="32"/>
<size from="49" to="49" total="98" count="2"/>
<size from="81" to="81" total="810" count="10"/>
<size from="257" to="257" total="2827" count="11"/>
<size from="689" to="689" total="689" count="1"/>
<size from="705" to="705" total="705" count="1"/>
<unsorted from="81" to="81" total="81" count="1"/>
</sizes>
<total type="fast" count="48" size="4320"/>
<total type="rest" count="27" size="118618"/>
<system type="current" size="135168"/>
<system type="max" size="135168"/>
<aspace type="total" size="135168"/>
<aspace type="mprotect" size="135168"/>
<aspace type="subheaps" size="1"/>
</heap>
<total type="fast" count="938" size="79392"/>
<total type="rest" count="700" size="4409469"/>
<total type="mmap" count="0" size="0"/>
<system type="current" size="15114240"/>
<system type="max" size="15114240"/>
<aspace type="total" size="15114240"/>
<aspace type="mprotect" size="15114240"/>
</malloc>
```
## API Clients <a id="icinga2-api-clients"></a>
After its initial release in 2015, community members

View File

@ -32,6 +32,7 @@ set(remote_SOURCES
infohandler.cpp infohandler.hpp
jsonrpc.cpp jsonrpc.hpp
jsonrpcconnection.cpp jsonrpcconnection.hpp jsonrpcconnection-heartbeat.cpp jsonrpcconnection-pki.cpp
mallocinfohandler.cpp mallocinfohandler.hpp
messageorigin.cpp messageorigin.hpp
modifyobjecthandler.cpp modifyobjecthandler.hpp
objectqueryhandler.cpp objectqueryhandler.hpp

View File

@ -0,0 +1,94 @@
/* Icinga 2 | (c) 2024 Icinga GmbH | GPLv2+ */
#include "base/defer.hpp"
#include "remote/filterutility.hpp"
#include "remote/httputility.hpp"
#include "remote/mallocinfohandler.hpp"
#include <cstddef>
#include <cstdio>
#include <string>
#ifdef HAVE_MALLOC_INFO
# include <errno.h>
# include <malloc.h>
#endif /* HAVE_MALLOC_INFO */
using namespace icinga;
REGISTER_URLHANDLER("/v1/debug/malloc_info", MallocInfoHandler);
bool MallocInfoHandler::HandleRequest(
AsioTlsStream&,
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context&,
HttpServerConnection&
)
{
namespace http = boost::beast::http;
if (url->GetPath().size() != 3) {
return false;
}
if (request.method() != http::verb::get) {
return false;
}
FilterUtility::CheckPermission(user, "debug");
#ifndef HAVE_MALLOC_INFO
HttpUtility::SendJsonError(response, params, 501, "malloc_info(3) not available.");
#else /* HAVE_MALLOC_INFO */
char* buf = nullptr;
size_t bufSize = 0;
FILE* f = nullptr;
Defer release ([&f, &buf]() {
if (f) {
(void)fclose(f);
}
free(buf);
});
f = open_memstream(&buf, &bufSize);
if (!f) {
auto error (errno);
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("open_memstream")
<< boost::errinfo_errno(error));
}
if (malloc_info(0, f)) {
auto error (errno);
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("malloc_info")
<< boost::errinfo_errno(error));
}
auto closeErr (fclose(f));
f = nullptr;
if (closeErr) {
auto error (errno);
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("fclose")
<< boost::errinfo_errno(error));
}
response.result(200);
response.set(http::field::content_type, "application/xml");
response.body() = std::string(buf, bufSize);
response.content_length(response.body().size());
#endif /* HAVE_MALLOC_INFO */
return true;
}

View File

@ -0,0 +1,27 @@
/* Icinga 2 | (c) 2024 Icinga GmbH | GPLv2+ */
#pragma once
#include "remote/httphandler.hpp"
namespace icinga
{
class MallocInfoHandler final : public HttpHandler
{
public:
DECLARE_PTR_TYPEDEFS(MallocInfoHandler);
bool HandleRequest(
AsioTlsStream& stream,
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc,
HttpServerConnection& server
) override;
};
}