sapling/eden/fs/utils/ProcessNameCache.h
Ailin Zhang 7f2329a3ff add space between command name and args when logging fetch heavy processes to Scuba
Summary:
Previously, fetch heavy event's cmdline was delimited by '\x00' when logged to Scuba. (for example: `grep--color=auto-rtest.`)
Now we replace \x00 with a space, so command name and args will be separated by space. ( `grep --color=auto -r test .` )

Reviewed By: kmancini

Differential Revision: D22772868

fbshipit-source-id: 4ab42e78c7bc786767eee3413b9586739a12e8ac
2020-07-31 11:42:51 -07:00

136 lines
3.9 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#pragma once
#include <folly/Synchronized.h>
#include <folly/container/F14Set.h>
#include <folly/futures/Promise.h>
#include <folly/synchronization/LifoSem.h>
#include <sys/types.h>
#include <chrono>
#include <map>
#include <string>
#include <vector>
namespace facebook {
namespace eden {
class ProcessNameCache {
public:
/**
* Create a cache that maintains process names until `expiry` has elapsed
* without them being referenced or observed.
*/
explicit ProcessNameCache(
std::chrono::nanoseconds expiry = std::chrono::minutes{5});
~ProcessNameCache();
/**
* Records a reference to a pid. This is called by performance-critical code.
* Refreshes the expiry on the given pid. The process name is read
* asynchronously on a background thread.
*
* If possible, the caller should avoid calling add() with a series of
* redundant pids.
*/
void add(pid_t pid);
/**
* Called rarely to produce a map of all non-expired pids to their executable
* names.
*/
std::map<pid_t, std::string> getAllProcessNames();
/**
* Called occassionally to produce the command line name of the pid. If the
* name has already been resolved this returns that name. Otherwise this will
* return nullopt. In the future it may wait for the name to be resolved.
*/
std::optional<std::string> getProcessName(pid_t pid);
/**
* Have the same functionality as getProcessName except that spaces are added
* between each part of the command line.
*/
std::optional<std::string> getSpacedProcessName(pid_t pid);
private:
struct ProcessName {
ProcessName(std::string n, std::chrono::steady_clock::duration d)
: name{std::move(n)}, lastAccess{d} {}
ProcessName(ProcessName&& other) noexcept
: name{std::move(other.name)}, lastAccess{other.lastAccess.load()} {}
ProcessName& operator=(ProcessName&& other) noexcept {
name = std::move(other.name);
lastAccess.store(other.lastAccess.load());
return *this;
}
std::string name;
mutable std::atomic<std::chrono::steady_clock::duration> lastAccess;
};
struct State {
std::unordered_map<pid_t, ProcessName> names;
// Allows periodic flushing of the expired names without quadratic-time
// insertion. waterLevel grows twice as fast as names.size() can, and when
// it exceeds names.size(), the name set is pruned.
size_t waterLevel = 0;
bool workerThreadShouldStop = false;
folly::F14FastSet<pid_t> addQueue;
std::vector<folly::Promise<std::map<pid_t, std::string>>> getQueue;
};
void clearExpired(std::chrono::steady_clock::duration now, State& state);
void processActions();
const std::chrono::nanoseconds expiry_;
const std::chrono::steady_clock::time_point startPoint_;
folly::Synchronized<State> state_;
folly::LifoSem sem_;
std::thread workerThread_;
};
namespace detail {
/**
* The number of digits required for a decimal representation of a pid.
*/
constexpr size_t kMaxDecimalPidLength = 10;
static_assert(sizeof(pid_t) <= 4);
/**
* A stack-allocated string with the contents /proc/<pid>/cmdline for any pid.
*/
using ProcPidCmdLine = std::array<
char,
6 /* /proc/ */ + kMaxDecimalPidLength + 8 /* /cmdline */ + 1 /* null */>;
/**
* Returns the ProcPidCmdLine for a given pid. The result is always
* null-terminated.
*/
ProcPidCmdLine getProcPidCmdLine(pid_t pid);
/**
* Given a pid, returns its executable name or <err:###> with the appropriate
* errno.
*
* readPidName only allocates if the resulting executable name does not fit in
* std::string's small string optimization, which is relatively rare for
* programs in /usr/bin.
*/
std::string readPidName(pid_t pid);
} // namespace detail
} // namespace eden
} // namespace facebook