sapling/eden/fs/utils/ProcessAccessLog.h
Zhengchao Liu 067abe2d55 collect memory and disk fetch counts
Summary: This adds counters for memory and disk counts in addition to import count so that we can understand cache hit rates during local investigation or output this in ActivityRecorder.

Reviewed By: genevievehelsel

Differential Revision: D29805637

fbshipit-source-id: 34261f91c33d6bd4bcb4b85b17d2e68360410896
2021-07-21 21:37:20 -07:00

111 lines
3.1 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 <type_traits>
#include "eden/fs/service/gen-cpp2/eden_types.h"
#include "eden/fs/utils/BucketedLog.h"
#include "eden/fs/utils/EnumValue.h"
namespace facebook {
namespace eden {
class ProcessNameCache;
struct ThreadLocalBucket;
/**
* An inexpensive mechanism for counting accesses by pids. Intended for counting
* channel and Thrift calls from external processes.
*
* The first time a thread calls recordAccess, that thread is then coupled to
* this particular ProcessAccessLog, even if it calls recordAccess on another
* ProcessAccessLog instance. Thus, use one ProcessAccessLog per pool of
* threads.
*/
class ProcessAccessLog {
public:
enum class AccessType : unsigned char {
FsChannelRead,
FsChannelWrite,
FsChannelOther,
FsChannelMemoryCacheImport,
FsChannelDiskCacheImport,
FsChannelBackingStoreImport,
Last,
};
explicit ProcessAccessLog(std::shared_ptr<ProcessNameCache> processNameCache);
~ProcessAccessLog();
/**
* Records an access by a process ID. The first call to recordAccess by a
* particular thread binds that thread to this access log. Future recordAccess
* calls on that thread will accumulate within this access log.
*
* Process IDs passed to recordAccess are also inserted into the
* ProcessNameCache.
*/
void recordAccess(pid_t pid, AccessType type);
void recordDuration(pid_t pid, std::chrono::nanoseconds duration);
/**
* Returns the number of times each pid was passed to recordAccess() in
* `lastNSeconds`.
*
* Note: ProcessAccessLog buckets by whole seconds, so this number should be
* considered an approximation.
*/
std::unordered_map<pid_t, AccessCounts> getAccessCounts(
std::chrono::seconds lastNSeconds);
private:
struct PerBucketAccessCounts {
size_t counts[enumValue(AccessType::Last)];
std::chrono::nanoseconds duration;
size_t& operator[](AccessType type) {
static_assert(std::is_unsigned_v<std::underlying_type_t<AccessType>>);
auto idx = enumValue(type);
XCHECK_LT(idx, enumValue(AccessType::Last));
return counts[idx];
}
};
// Data for one second.
struct Bucket {
void clear();
void add(pid_t pid, bool& isNew, AccessType type);
void add(pid_t pid, bool& isNew, std::chrono::nanoseconds duration);
void merge(const Bucket& other);
std::unordered_map<pid_t, PerBucketAccessCounts> accessCountsByPid;
};
// Keep up to ten seconds of data, but use a power of two so BucketedLog
// generates smaller, faster code.
static constexpr uint64_t kBucketCount = 16;
using Buckets = BucketedLog<Bucket, kBucketCount>;
struct State {
Buckets buckets;
};
const std::shared_ptr<ProcessNameCache> processNameCache_;
folly::Synchronized<State> state_;
uint64_t getSecondsSinceEpoch();
ThreadLocalBucket* getTlb();
friend struct ThreadLocalBucket;
};
} // namespace eden
} // namespace facebook