mirror of
https://github.com/facebook/sapling.git
synced 2024-10-08 07:49:11 +03:00
7bc8e0cbe0
Summary: Eden is accessed by many short-lived processes. We want to show their names in `eden top` so efficiently try to grab the names of pids. This code will be used in a later diff. Reviewed By: strager Differential Revision: D9477181 fbshipit-source-id: 28998ac8bf7b804a3bd4944fbda4c7d1a0918312
109 lines
2.9 KiB
C++
109 lines
2.9 KiB
C++
/*
|
|
* Copyright (c) 2018-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
#pragma once
|
|
|
|
#include <folly/Synchronized.h>
|
|
#include <sys/types.h>
|
|
#include <chrono>
|
|
#include <map>
|
|
#include <string>
|
|
|
|
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});
|
|
|
|
/**
|
|
* Records a reference to a pid. This is called by performance-critical code.
|
|
* Refreshes the expiry on the given pid.
|
|
*
|
|
* 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();
|
|
|
|
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.
|
|
size_t waterLevel = 0;
|
|
};
|
|
|
|
void clearExpired(std::chrono::steady_clock::duration now, State& state);
|
|
|
|
const std::chrono::nanoseconds expiry_;
|
|
const std::chrono::steady_clock::time_point startPoint_;
|
|
folly::Synchronized<State> state_;
|
|
};
|
|
|
|
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>/exe for any pid.
|
|
*/
|
|
using ProcPidExe = std::array<
|
|
char,
|
|
6 /* /proc/ */ + kMaxDecimalPidLength + 4 /* /exe */ + 1 /* null */>;
|
|
|
|
/**
|
|
* Returns the ProcPidExe for a given pid. The result is always null-terminated.
|
|
*/
|
|
ProcPidExe getProcPidExe(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
|