Skip to content

Commit

Permalink
report process name and uptime for each connected client
Browse files Browse the repository at this point in the history
Summary:
It's helpful to know the set of connected clients, when they
connected, and what their status is. Include pid and command line and
uptime in `watchman debug-status`.

Reviewed By: kmancini

Differential Revision: D37436553

fbshipit-source-id: dc9488dab4a0b2e1fd6860a65d3a73bdd5a1e712
  • Loading branch information
chadaustin authored and facebook-github-bot committed Jul 9, 2022
1 parent 1f3a479 commit 7918006
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 2 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Expand Up @@ -342,10 +342,10 @@ find_package(
find_package(LibEvent REQUIRED)
get_filename_component(LIBEVENT_LIBDIR "${LIBEVENT_LIB}" DIRECTORY)
link_directories(${LIBEVENT_LIBDIR})
find_package(edencommon CONFIG REQUIRED)
find_package(fmt CONFIG REQUIRED)
find_package(folly CONFIG REQUIRED)


if (ENABLE_EDEN_SUPPORT)
find_package(fizz CONFIG REQUIRED)
find_package(wangle CONFIG REQUIRED)
Expand Down Expand Up @@ -704,6 +704,7 @@ target_link_libraries(
jansson
wildmatch
third_party_deps
edencommon::utils
)

if (WIN32)
Expand Down
1 change: 1 addition & 0 deletions build/fbcode_builder/manifests/watchman
Expand Up @@ -13,6 +13,7 @@ builder = cmake
[dependencies]
boost
cpptoml
edencommon
fb303
fbthrift
folly
Expand Down
23 changes: 22 additions & 1 deletion watchman/Client.cpp
Expand Up @@ -23,6 +23,17 @@ namespace watchman {

namespace {

using namespace facebook::eden;

ProcessNameCache& getProcessNameCache() {
static auto* pnc = new ProcessNameCache;
return *pnc;
}

ProcessNameHandle lookupProcessName(pid_t pid) {
return getProcessNameCache().lookup(pid);
}

constexpr size_t kResponseLogLimit = 0;

folly::Synchronized<std::unordered_set<UserClient*>> clients;
Expand Down Expand Up @@ -209,7 +220,10 @@ void UserClient::create(std::unique_ptr<watchman_stream> stm) {
}

UserClient::UserClient(PrivateBadge, std::unique_ptr<watchman_stream> stm)
: Client(std::move(stm)) {
: Client{std::move(stm)},
since_{std::chrono::system_clock::now()},
peerPid_{this->stm->getPeerProcessID()},
peerName_{lookupProcessName(peerPid_)} {
clients.wlock()->insert(this);
}

Expand Down Expand Up @@ -246,6 +260,13 @@ std::vector<ClientDebugStatus> UserClient::getStatusForAllClients() {
ClientDebugStatus UserClient::getDebugStatus() const {
ClientDebugStatus rv;
rv.state = status_.getName();
if (peerPid_) {
rv.peer.emplace();
rv.peer->pid = peerPid_;
// May briefly, once, block on the ProcessNameCache thread.
rv.peer->name = peerName_.get();
}
rv.since = std::chrono::system_clock::to_time_t(since_);
return rv;
}

Expand Down
24 changes: 24 additions & 0 deletions watchman/Client.h
Expand Up @@ -6,9 +6,14 @@
*/

#pragma once

#include <eden/common/utils/ProcessNameCache.h>
#include <fmt/format.h>

#include <chrono>
#include <deque>
#include <unordered_map>

#include "watchman/Clock.h"
#include "watchman/CommandRegistry.h"
#include "watchman/Logging.h"
Expand Down Expand Up @@ -154,12 +159,27 @@ class ClientStatus {
std::atomic<State> state_{THREAD_STARTING};
};

struct PeerInfo : serde::Object {
int32_t pid;
std::string name;

template <typename X>
void map(X& x) {
x("pid", pid);
x("name", name);
}
};

struct ClientDebugStatus : serde::Object {
std::string state;
std::optional<PeerInfo> peer;
std::optional<int64_t> since;

template <typename X>
void map(X& x) {
x("state", state);
x("peer", peer);
x("since", since);
}
};

Expand Down Expand Up @@ -214,6 +234,10 @@ class UserClient final : public Client {

void clientThread() noexcept;

const std::chrono::system_clock::time_point since_;
const pid_t peerPid_;
const facebook::eden::ProcessNameHandle peerName_;

ClientStatus status_;
};

Expand Down
37 changes: 37 additions & 0 deletions watchman/cmds/debug.cpp
Expand Up @@ -5,7 +5,10 @@
* LICENSE file in the root directory of this source tree.
*/

#include <fmt/chrono.h>
#include <folly/String.h>
#include <folly/chrono/Conv.h>
#include <folly/system/Shell.h>
#include <unordered_map>
#include "watchman/Client.h"
#include "watchman/InMemoryView.h"
Expand Down Expand Up @@ -227,6 +230,28 @@ W_CMD_REG(
CMD_DAEMON,
w_cmd_realpath_root);

namespace {

std::string shellQuoteCommand(std::string_view command) {
std::vector<std::string> argv;
folly::split('\0', command, argv);

// Every argument in a command line is null-terminated. Remove the last, empty
// argument.
if (argv.size() && argv.back().empty()) {
argv.pop_back();
}

for (auto& arg : argv) {
// TODO: shellQuote is not particularly good. It always brackets with ' and
// does not handle non-printable characters. We should write our own.
arg = folly::shellQuote(arg);
}
return folly::join(' ', argv);
}

} // namespace

struct DebugStatusCommand : PrettyCommand<DebugStatusCommand> {
static constexpr std::string_view name = "debug-status";

Expand Down Expand Up @@ -273,7 +298,19 @@ struct DebugStatusCommand : PrettyCommand<DebugStatusCommand> {

fmt::print("CLIENTS\n-------\n");
for (auto& client : response.clients) {
if (client.peer) {
fmt::print(
"{}: {}\n", client.peer->pid, shellQuoteCommand(client.peer->name));
} else {
fmt::print("unknown peer\n");
}
if (client.since) {
fmt::print(
" - since: {}\n",
std::chrono::system_clock::from_time_t(client.since.value()));
}
fmt::print(" - state: {}\n", client.state);
fmt::print("\n");
}
}
};
Expand Down

0 comments on commit 7918006

Please sign in to comment.