mgr: add heap admin socket command

the mon, osd and mds register a "heap" asok command that forwards to
ceph_heap_profiler_handle_command, but the mgr never did, so
`ceph daemon mgr.<id> heap stats` returns "no valid command found".
that leaves no way to tcmalloc-profile a leaking mgr through its admin
socket, which is what chasing mgr memory growth needs.

register the same command on MgrStandby's admin socket. its hook lives
for the whole process, so it works on both the standby and the active
mgr. heap output goes to the bufferlist, as on the other daemons, while
status keeps using the formatter.

Signed-off-by: Kefu Chai <k.chai@proxmox.com>
This commit is contained in:
Kefu Chai 2026-06-30 09:29:18 +08:00
parent c11c7144fc
commit d8dde402b6
2 changed files with 35 additions and 3 deletions

View File

@ -17,7 +17,10 @@
#include "common/errno.h"
#include "common/signal.h"
#include "common/cmdparse.h"
#include "include/compat.h"
#include "include/str_list.h"
#include "perfglue/heap_profiler.h"
#include "include/stringify.h"
#include "global/global_context.h"
@ -42,6 +45,7 @@ using std::map;
using std::string;
using std::vector;
using namespace std::literals;
using ceph::common::cmd_getval;
class MgrHook : public AdminSocketHook {
MgrStandby* mgr;
@ -55,7 +59,7 @@ public:
bufferlist& outbl) override {
int r = 0;
try {
r = mgr->asok_command(admin_command, cmdmap, f, errss);
r = mgr->asok_command(admin_command, cmdmap, f, errss, outbl);
} catch (const TOPNSPC::common::bad_cmd_get& e) {
errss << e.what();
r = -EINVAL;
@ -141,13 +145,32 @@ void MgrStandby::handle_conf_change(
}
}
int MgrStandby::asok_command(std::string_view cmd, const cmdmap_t& cmdmap, Formatter* f, std::ostream& errss)
int MgrStandby::asok_command(std::string_view cmd, const cmdmap_t& cmdmap,
Formatter* f, std::ostream& errss,
ceph::buffer::list& outbl)
{
dout(10) << __func__ << ": " << cmd << dendl;
if (cmd == "status") {
f->open_object_section("status");
f->close_section();
return 0;
} else if (cmd == "heap") {
if (!ceph_using_tcmalloc()) {
errss << "could not issue heap profiler command -- not using tcmalloc!";
return -EOPNOTSUPP;
}
std::string heapcmd;
cmd_getval(cmdmap, "heapcmd", heapcmd);
std::vector<std::string> cmd_vec;
get_str_vec(heapcmd, cmd_vec);
std::string val;
if (cmd_getval(cmdmap, "value", val)) {
cmd_vec.push_back(val);
}
std::ostringstream outss;
ceph_heap_profiler_handle_command(cmd_vec, outss);
outbl.append(outss.str());
return 0;
} else {
return -ENOSYS;
}
@ -182,6 +205,14 @@ int MgrStandby::init()
{
int r = admin_socket->register_command("status", asok_hook.get(), "show status");
ceph_assert(r == 0);
r = admin_socket->register_command(
"heap " \
"name=heapcmd,type=CephChoices,strings=" \
"dump|start_profiler|stop_profiler|release|get_release_rate|set_release_rate|stats " \
"name=value,type=CephString,req=false",
asok_hook.get(),
"show heap usage info (available only if compiled with tcmalloc)");
ceph_assert(r == 0);
}
poolctx.start(2);

View File

@ -39,7 +39,8 @@ public:
std::vector<std::string> get_tracked_keys() const noexcept override;
void handle_conf_change(const ConfigProxy& conf,
const std::set <std::string> &changed) override;
int asok_command(std::string_view cmd, const cmdmap_t& cmdmap, Formatter* f, std::ostream& errss);
int asok_command(std::string_view cmd, const cmdmap_t& cmdmap, Formatter* f,
std::ostream& errss, ceph::buffer::list& outbl);
protected:
ceph::async::io_context_pool poolctx;