[scripts] Remove perf-counters (#35308)

This commit is contained in:
Sebastian "Sebbie" Silbermann
2025-12-06 12:33:42 +01:00
committed by GitHub
parent 2cb08e65b3
commit c0b7c0d31f
12 changed files with 0 additions and 1922 deletions

View File

@@ -1,3 +0,0 @@
build/jsc-perf: src/*
mkdir -p build
g++ -std=c++11 -I/usr/include/webkitgtk-1.0/ -ljavascriptcoregtk-1.0 src/jsc-perf.cpp src/hardware-counter.cpp src/thread-local.cpp -o build/jsc-perf

View File

@@ -1,16 +0,0 @@
# perf-counters
Lightweight bindings to Linux perf event counters.
```
$ node
> var PerfCounters = require('perf-counters');
> PerfCounters.init();
> var start = PerfCounters.getCounters(); console.log('test'); var end = PerfCounters.getCounters();
test
> start
{ instructions: 1382, loads: 421, stores: 309 }
> end
{ instructions: 647633, loads: 195771, stores: 133246 }
>
```

View File

@@ -1,15 +0,0 @@
{
"targets": [
{
"target_name": "perfcounters",
"sources": [
"src/hardware-counter.cpp",
"src/perf-counters.cpp",
"src/thread-local.cpp",
],
"cflags": [
"-Wno-sign-compare",
],
},
],
}

View File

@@ -1,3 +0,0 @@
'use strict';
module.exports = require('bindings')('perfcounters');

View File

@@ -1,10 +0,0 @@
{
"name": "perf-counters",
"version": "0.1.2",
"description": "Lightweight bindings to Linux perf event counters.",
"main": "index.js",
"license": "MIT",
"dependencies": {
"bindings": "^1.2.1"
}
}

View File

@@ -1,469 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "hardware-counter.h"
#ifndef NO_HARDWARE_COUNTERS
#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/unistd.h>
#include <sys/prctl.h>
#include <linux/perf_event.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
IMPLEMENT_THREAD_LOCAL_NO_CHECK(HardwareCounter,
HardwareCounter::s_counter);
static bool s_recordSubprocessTimes = false;
static bool s_profileHWEnable;
static std::string s_profileHWEvents;
static inline bool useCounters() {
#ifdef VALGRIND
return false;
#else
return s_profileHWEnable;
#endif
}
class HardwareCounterImpl {
public:
HardwareCounterImpl(int type, unsigned long config,
const char* desc = nullptr)
: m_desc(desc ? desc : ""), m_err(0), m_fd(-1), inited(false) {
memset (&pe, 0, sizeof (struct perf_event_attr));
pe.type = type;
pe.size = sizeof (struct perf_event_attr);
pe.config = config;
pe.inherit = s_recordSubprocessTimes;
pe.disabled = 1;
pe.pinned = 0;
pe.exclude_kernel = 0;
pe.exclude_hv = 1;
pe.read_format =
PERF_FORMAT_TOTAL_TIME_ENABLED|PERF_FORMAT_TOTAL_TIME_RUNNING;
}
~HardwareCounterImpl() {
close();
}
void init_if_not() {
/*
* perf_event_open(struct perf_event_attr *hw_event_uptr, pid_t pid,
* int cpu, int group_fd, unsigned long flags)
*/
if (inited) return;
inited = true;
m_fd = syscall(__NR_perf_event_open, &pe, 0, -1, -1, 0);
if (m_fd < 0) {
// Logger::Verbose("perf_event_open failed with: %s",
// folly::errnoStr(errno).c_str());
m_err = -1;
return;
}
if (ioctl(m_fd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
// Logger::Warning("perf_event failed to enable: %s",
// folly::errnoStr(errno).c_str());
close();
m_err = -1;
return;
}
reset();
}
int64_t read() {
uint64_t values[3];
if (readRaw(values)) {
if (!values[2]) return 0;
int64_t value = (double)values[0] * values[1] / values[2];
return value + extra;
}
return 0;
}
void incCount(int64_t amount) {
extra += amount;
}
bool readRaw(uint64_t* values) {
if (m_err || !useCounters()) return false;
init_if_not();
if (m_fd > 0) {
/*
* read the count + scaling values
*
* It is not necessary to stop an event to read its value
*/
auto ret = ::read(m_fd, values, sizeof(*values) * 3);
if (ret == sizeof(*values) * 3) {
values[0] -= reset_values[0];
values[1] -= reset_values[1];
values[2] -= reset_values[2];
return true;
}
}
return false;
}
void reset() {
if (m_err || !useCounters()) return;
init_if_not();
extra = 0;
if (m_fd > 0) {
if (ioctl (m_fd, PERF_EVENT_IOC_RESET, 0) < 0) {
// Logger::Warning("perf_event failed to reset with: %s",
// folly::errnoStr(errno).c_str());
m_err = -1;
return;
}
auto ret = ::read(m_fd, reset_values, sizeof(reset_values));
if (ret != sizeof(reset_values)) {
// Logger::Warning("perf_event failed to reset with: %s",
// folly::errnoStr(errno).c_str());
m_err = -1;
return;
}
}
}
public:
std::string m_desc;
int m_err;
private:
int m_fd;
struct perf_event_attr pe;
bool inited;
uint64_t reset_values[3];
uint64_t extra{0};
void close() {
if (m_fd > 0) {
::close(m_fd);
m_fd = -1;
}
}
};
class InstructionCounter : public HardwareCounterImpl {
public:
InstructionCounter() :
HardwareCounterImpl(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS) {}
};
class LoadCounter : public HardwareCounterImpl {
public:
LoadCounter() :
HardwareCounterImpl(PERF_TYPE_HW_CACHE,
(PERF_COUNT_HW_CACHE_L1D | ((PERF_COUNT_HW_CACHE_OP_READ) << 8))) {}
};
class StoreCounter : public HardwareCounterImpl {
public:
StoreCounter() :
HardwareCounterImpl(PERF_TYPE_HW_CACHE,
PERF_COUNT_HW_CACHE_L1D | ((PERF_COUNT_HW_CACHE_OP_WRITE) << 8)) {}
};
HardwareCounter::HardwareCounter()
: m_countersSet(false) {
m_instructionCounter.reset(new InstructionCounter());
if (s_profileHWEvents.empty()) {
m_loadCounter.reset(new LoadCounter());
m_storeCounter.reset(new StoreCounter());
} else {
m_countersSet = true;
setPerfEvents(s_profileHWEvents);
}
}
HardwareCounter::~HardwareCounter() {
}
void HardwareCounter::Init(bool enable, const std::string& events,
bool subProc) {
s_profileHWEnable = enable;
s_profileHWEvents = events;
s_recordSubprocessTimes = subProc;
}
void HardwareCounter::Reset() {
s_counter->reset();
}
void HardwareCounter::reset() {
m_instructionCounter->reset();
if (!m_countersSet) {
m_storeCounter->reset();
m_loadCounter->reset();
}
for (unsigned i = 0; i < m_counters.size(); i++) {
m_counters[i]->reset();
}
}
int64_t HardwareCounter::GetInstructionCount() {
return s_counter->getInstructionCount();
}
int64_t HardwareCounter::getInstructionCount() {
return m_instructionCounter->read();
}
int64_t HardwareCounter::GetLoadCount() {
return s_counter->getLoadCount();
}
int64_t HardwareCounter::getLoadCount() {
return m_loadCounter->read();
}
int64_t HardwareCounter::GetStoreCount() {
return s_counter->getStoreCount();
}
int64_t HardwareCounter::getStoreCount() {
return m_storeCounter->read();
}
void HardwareCounter::IncInstructionCount(int64_t amount) {
s_counter->m_instructionCounter->incCount(amount);
}
void HardwareCounter::IncLoadCount(int64_t amount) {
if (!s_counter->m_countersSet) {
s_counter->m_loadCounter->incCount(amount);
}
}
void HardwareCounter::IncStoreCount(int64_t amount) {
if (!s_counter->m_countersSet) {
s_counter->m_storeCounter->incCount(amount);
}
}
struct PerfTable perfTable[] = {
/* PERF_TYPE_HARDWARE events */
#define PC(n) PERF_TYPE_HARDWARE, PERF_COUNT_HW_ ## n
{ "cpu-cycles", PC(CPU_CYCLES) },
{ "cycles", PC(CPU_CYCLES) },
{ "instructions", PC(INSTRUCTIONS) },
{ "cache-references", PC(CACHE_REFERENCES) },
{ "cache-misses", PC(CACHE_MISSES) },
{ "branch-instructions", PC(BRANCH_INSTRUCTIONS) },
{ "branches", PC(BRANCH_INSTRUCTIONS) },
{ "branch-misses", PC(BRANCH_MISSES) },
{ "bus-cycles", PC(BUS_CYCLES) },
{ "stalled-cycles-frontend", PC(STALLED_CYCLES_FRONTEND) },
{ "stalled-cycles-backend", PC(STALLED_CYCLES_BACKEND) },
/* PERF_TYPE_HW_CACHE hw_cache_id */
#define PCC(n) PERF_TYPE_HW_CACHE, PERF_COUNT_HW_CACHE_ ## n
{ "L1-dcache-", PCC(L1D) },
{ "L1-icache-", PCC(L1I) },
{ "LLC-", PCC(LL) },
{ "dTLB-", PCC(DTLB) },
{ "iTLB-", PCC(ITLB) },
{ "branch-", PCC(BPU) },
/* PERF_TYPE_HW_CACHE hw_cache_op, hw_cache_result */
#define PCCO(n, m) PERF_TYPE_HW_CACHE, \
((PERF_COUNT_HW_CACHE_OP_ ## n) << 8 | \
(PERF_COUNT_HW_CACHE_RESULT_ ## m) << 16)
{ "loads", PCCO(READ, ACCESS) },
{ "load-misses", PCCO(READ, MISS) },
{ "stores", PCCO(WRITE, ACCESS) },
{ "store-misses", PCCO(WRITE, MISS) },
{ "prefetches", PCCO(PREFETCH, ACCESS) },
{ "prefetch-misses", PCCO(PREFETCH, MISS) }
};
static int findEvent(const char *event, struct PerfTable *t,
int len, int *match_len) {
int i;
for (i = 0; i < len; i++) {
if (!strncmp(event, t[i].name, strlen(t[i].name))) {
*match_len = strlen(t[i].name);
return i;
}
}
return -1;
}
#define CPUID_STEPPING(x) ((x) & 0xf)
#define CPUID_MODEL(x) (((x) & 0xf0) >> 4)
#define CPUID_FAMILY(x) (((x) & 0xf00) >> 8)
#define CPUID_TYPE(x) (((x) & 0x3000) >> 12)
// hack to get LLC counters on perflab frc machines
static bool isIntelE5_2670() {
#ifdef __x86_64__
unsigned long x;
asm volatile ("cpuid" : "=a"(x): "a"(1) : "ebx", "ecx", "edx");
return CPUID_STEPPING(x) == 6 && CPUID_MODEL(x) == 0xd
&& CPUID_FAMILY(x) == 6 && CPUID_TYPE(x) == 0;
#else
return false;
#endif
}
static void checkLLCHack(const char* event, uint32_t& type, uint64_t& config) {
if (!strncmp(event, "LLC-load", 8) && isIntelE5_2670()) {
type = PERF_TYPE_RAW;
if (!strncmp(&event[4], "loads", 5)) {
config = 0x534f2e;
} else if (!strncmp(&event[4], "load-misses", 11)) {
config = 0x53412e;
}
}
}
bool HardwareCounter::addPerfEvent(const char* event) {
uint32_t type = 0;
uint64_t config = 0;
int i, match_len;
bool found = false;
const char* ev = event;
while ((i = findEvent(ev, perfTable,
sizeof(perfTable)/sizeof(struct PerfTable),
&match_len))
!= -1) {
if (!found) {
found = true;
type = perfTable[i].type;
} else if (type != perfTable[i].type) {
// Logger::Warning("failed to find perf event: %s", event);
return false;
}
config |= perfTable[i].config;
ev = &ev[match_len];
}
checkLLCHack(event, type, config);
// Check if we have a raw spec.
if (!found && event[0] == 'r' && event[1] != 0) {
config = strtoull(event + 1, const_cast<char**>(&ev), 16);
if (*ev == 0) {
found = true;
type = PERF_TYPE_RAW;
}
}
if (!found || *ev) {
// Logger::Warning("failed to find perf event: %s", event);
return false;
}
std::unique_ptr<HardwareCounterImpl> hwc(
new HardwareCounterImpl(type, config, event));
if (hwc->m_err) {
// Logger::Warning("failed to set perf event: %s", event);
return false;
}
m_counters.emplace_back(std::move(hwc));
if (!m_countersSet) {
// reset load and store counters. This is because
// perf does not seem to handle more than three counters
// very well.
m_loadCounter.reset();
m_storeCounter.reset();
m_countersSet = true;
}
return true;
}
bool HardwareCounter::eventExists(const char *event) {
// hopefully m_counters set is small, so a linear scan does not hurt
for(unsigned i = 0; i < m_counters.size(); i++) {
if (!strcmp(event, m_counters[i]->m_desc.c_str())) {
return true;
}
}
return false;
}
bool HardwareCounter::setPerfEvents(std::string sevents) {
// Make a copy of the string for use with strtok.
auto const sevents_buf = static_cast<char*>(malloc(sevents.size() + 1));
memcpy(sevents_buf, sevents.data(), sevents.size());
sevents_buf[sevents.size()] = '\0';
char* strtok_buf = nullptr;
char* s = strtok_r(sevents_buf, ",", &strtok_buf);
bool success = true;
while (s) {
if (!eventExists(s) && !addPerfEvent(s)) {
success = false;
break;
}
s = strtok_r(nullptr, ",", &strtok_buf);
}
free(sevents_buf);
return success;
}
bool HardwareCounter::SetPerfEvents(std::string events) {
return s_counter->setPerfEvents(events);
}
void HardwareCounter::clearPerfEvents() {
m_counters.clear();
}
void HardwareCounter::ClearPerfEvents() {
s_counter->clearPerfEvents();
}
const std::string
s_instructions("instructions"),
s_loads("loads"),
s_stores("stores");
void HardwareCounter::getPerfEvents(PerfEventCallback f, void* data) {
f(s_instructions, getInstructionCount(), data);
if (!m_countersSet) {
f(s_loads, getLoadCount(), data);
f(s_stores, getStoreCount(), data);
}
for (unsigned i = 0; i < m_counters.size(); i++) {
f(m_counters[i]->m_desc, m_counters[i]->read(), data);
}
}
void HardwareCounter::GetPerfEvents(PerfEventCallback f, void* data) {
s_counter->getPerfEvents(f, data);
}
///////////////////////////////////////////////////////////////////////////////
}
#else // NO_HARDWARE_COUNTERS
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
HardwareCounter HardwareCounter::s_counter;
///////////////////////////////////////////////////////////////////////////////
}
#endif // NO_HARDWARE_COUNTERS

View File

@@ -1,108 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#ifndef incl_HPHP_UTIL_HARDWARE_COUNTER_H_
#define incl_HPHP_UTIL_HARDWARE_COUNTER_H_
#include "thread-local.h"
#include <cstdint>
#include <memory>
#include <vector>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
#ifndef NO_HARDWARE_COUNTERS
class InstructionCounter;
class LoadCounter;
class StoreCounter;
struct PerfTable {
const char* name;
uint32_t type;
uint64_t config;
};
class HardwareCounterImpl;
class HardwareCounter {
public:
HardwareCounter();
~HardwareCounter();
static void Reset();
static int64_t GetInstructionCount();
static int64_t GetLoadCount();
static int64_t GetStoreCount();
static bool SetPerfEvents(std::string events);
static void IncInstructionCount(int64_t amount);
static void IncLoadCount(int64_t amount);
static void IncStoreCount(int64_t amount);
typedef void (*PerfEventCallback)(const std::string&, int64_t, void*);
static void GetPerfEvents(PerfEventCallback f, void* data);
static void ClearPerfEvents();
static void Init(bool enable, const std::string& events, bool subProc);
static DECLARE_THREAD_LOCAL_NO_CHECK(HardwareCounter, s_counter);
bool m_countersSet{false};
private:
void reset();
int64_t getInstructionCount();
int64_t getLoadCount();
int64_t getStoreCount();
bool eventExists(const char* event);
bool addPerfEvent(const char* event);
bool setPerfEvents(std::string events);
void getPerfEvents(PerfEventCallback f, void* data);
void clearPerfEvents();
std::unique_ptr<InstructionCounter> m_instructionCounter;
std::unique_ptr<LoadCounter> m_loadCounter;
std::unique_ptr<StoreCounter> m_storeCounter;
std::vector<std::unique_ptr<HardwareCounterImpl>> m_counters;
};
#else // NO_HARDWARE_COUNTERS
/* Stub implementation for platforms without hardware counters (non-linux)
* This mock class pretends to track performance events, but just returns
* static values, so it doesn't even need to worry about thread safety
* for the one static instance of itself.
*/
class HardwareCounter {
public:
HardwareCounter() : m_countersSet(false) { }
~HardwareCounter() { }
static void Reset() { }
static int64_t GetInstructionCount() { return 0; }
static int64_t GetLoadCount() { return 0; }
static int64_t GetStoreCount() { return 0; }
static bool SetPerfEvents(folly::StringPiece events) { return false; }
static void IncInstructionCount(int64_t amount) {}
static void IncLoadCount(int64_t amount) {}
static void IncStoreCount(int64_t amount) {}
typedef void (*PerfEventCallback)(const std::string&, int64_t, void*);
static void GetPerfEvents(PerfEventCallback f, void* data) { }
static void ClearPerfEvents() { }
static void Init(bool enable, const std::string& events, bool subProc) {}
// Normally exposed by DECLARE_THREAD_LOCAL_NO_CHECK
void getCheck() { }
void destroy() { }
static HardwareCounter s_counter;
bool m_countersSet;
};
#endif // NO_HARDWARE_COUNTERS
///////////////////////////////////////////////////////////////////////////////
}
#endif

View File

@@ -1,202 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fstream>
#include <iostream>
#include <string>
#include <JavaScriptCore/JavaScript.h>
#include "hardware-counter.h"
using HPHP::HardwareCounter;
void add_native_hook(
JSContextRef ctx,
JSObjectRef obj,
const char *name,
JSObjectCallAsFunctionCallback hook
) {
JSStringRef jsName = JSStringCreateWithUTF8CString(name);
JSObjectSetProperty(
ctx,
obj,
jsName,
JSObjectMakeFunctionWithCallback(ctx, jsName, hook),
kJSPropertyAttributeNone,
NULL
);
JSStringRelease(jsName);
}
static void fprint_value(
FILE *file,
JSContextRef context,
JSValueRef obj
) {
JSStringRef jsStr = JSValueToStringCopy(context, obj, NULL);
size_t size = JSStringGetMaximumUTF8CStringSize(jsStr);
char *str = (char *) calloc(
size,
1
);
JSStringGetUTF8CString(
jsStr,
str,
size
);
JSStringRelease(jsStr);
fprintf(file, "%s", str);
free(str);
}
static JSValueRef js_print(
JSContextRef context,
JSObjectRef object,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception
) {
for (int i = 0; i < argumentCount; i++) {
if (i != 0) {
printf(" ");
}
fprint_value(stdout, context, arguments[i]);
}
printf("\n");
return JSValueMakeUndefined(context);
}
static JSValueRef js_perf_counters_init(
JSContextRef context,
JSObjectRef object,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception
) {
// TODO: Allow customizing recorded events
bool enable = true;
std::string events = "";
bool recordSubprocesses = false;
HardwareCounter::Init(enable, events, recordSubprocesses);
HardwareCounter::s_counter.getCheck();
return JSValueMakeUndefined(context);
}
static JSValueRef js_perf_counters_get_counters(
JSContextRef context,
JSObjectRef object,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception
) {
JSObjectRef result = JSObjectMake(context, NULL, NULL);
std::pair<JSContextRef, JSObjectRef> pair(context, result);
HardwareCounter::GetPerfEvents(
[](const std::string& key, int64_t value, void* data) {
std::pair<JSContextRef, JSObjectRef>& pair =
*reinterpret_cast<std::pair<JSContextRef, JSObjectRef>*>(data);
JSContextRef context = pair.first;
JSObjectRef result = pair.second;
JSObjectSetProperty(
context,
result,
JSStringCreateWithUTF8CString(key.c_str()),
JSValueMakeNumber(context, value),
kJSPropertyAttributeNone,
NULL
);
},
&pair);
return result;
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "usage: jsc-runner file\n");
exit(1);
}
char *filename = argv[1];
std::ifstream ifs(filename);
if (ifs.fail()) {
std::cerr << "Error opening \"" << filename << "\": " << strerror(errno) << "\n";
exit(1);
}
std::string script(
(std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>())
);
JSStringRef jsScript = JSStringCreateWithUTF8CString(script.c_str());
JSStringRef jsURL = JSStringCreateWithUTF8CString(argv[1]);
JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
add_native_hook(
ctx,
JSContextGetGlobalObject(ctx),
"print",
js_print
);
JSObjectRef jsPerfCounters = JSObjectMake(ctx, NULL, NULL);
add_native_hook(
ctx,
jsPerfCounters,
"init",
js_perf_counters_init
);
add_native_hook(
ctx,
jsPerfCounters,
"getCounters",
js_perf_counters_get_counters
);
JSObjectSetProperty(
ctx,
JSContextGetGlobalObject(ctx),
JSStringCreateWithUTF8CString("PerfCounters"),
jsPerfCounters,
kJSPropertyAttributeNone,
NULL
);
JSValueRef jsError = NULL;
JSValueRef result = JSEvaluateScript(
ctx,
jsScript,
NULL,
jsURL,
0,
&jsError
);
if (!result) {
fprintf(stderr, "Exception: ");
fprint_value(stderr, ctx, jsError);
fprintf(stderr, "\n");
JSStringRef jsStackStr = JSStringCreateWithUTF8CString("stack");
if (JSValueIsObject(ctx, jsError)) {
JSValueRef jsStack = JSObjectGetProperty(ctx, (JSObjectRef)jsError, jsStackStr, NULL);
JSStringRelease(jsStackStr);
fprint_value(stderr, ctx, jsStack);
fprintf(stderr, "\n");
}
exit(1);
}
JSGlobalContextRelease(ctx);
}

View File

@@ -1,53 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <node.h>
#include "hardware-counter.h"
namespace PerfCounters {
using HPHP::HardwareCounter;
void Init(const v8::FunctionCallbackInfo<v8::Value>& args) {
// TODO: Allow customizing recorded events
bool enable = true;
std::string events = "";
bool recordSubprocesses = false;
HardwareCounter::Init(enable, events, recordSubprocesses);
HardwareCounter::s_counter.getCheck();
}
void GetCounters(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::Local<v8::Object> obj = v8::Object::New(isolate);
std::pair<v8::Isolate*, v8::Local<v8::Object>> pair(isolate, obj);
HardwareCounter::GetPerfEvents(
[](const std::string& key, int64_t value, void* data) {
std::pair<v8::Isolate*, v8::Local<v8::Object>>& pair =
*reinterpret_cast<std::pair<v8::Isolate*, v8::Local<v8::Object>>*>(data);
v8::Isolate* isolate = pair.first;
v8::Local<v8::Object> obj = pair.second;
obj->Set(
v8::String::NewFromUtf8(isolate, key.c_str()),
v8::Number::New(isolate, value)
);
},
&pair);
args.GetReturnValue().Set(obj);
}
void InitModule(v8::Local<v8::Object> exports) {
NODE_SET_METHOD(exports, "init", Init);
NODE_SET_METHOD(exports, "getCounters", GetCounters);
}
NODE_MODULE(perfcounters, InitModule)
}

View File

@@ -1,184 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#ifndef incl_HPHP_PORTABILITY_H_
#define incl_HPHP_PORTABILITY_H_
// From folly/Likely.h
#if defined(__GNUC__) && __GNUC__ >= 4
#define LIKELY(x) (__builtin_expect((x), 1))
#define UNLIKELY(x) (__builtin_expect((x), 0))
#else
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#endif
//////////////////////////////////////////////////////////////////////
/*
* Various macros to make certain things conditional on either
* compiler or architecture.
*
* Currently we don't *really* compile on anything other than gcc or
* sometimes clang, and there are some parts of the code using
* __attribute__ stuff directly, but some things go through these
* macros to make it maybe easier to change later.
*/
//////////////////////////////////////////////////////////////////////
// TODO: does clang define __GNUC__ ?
#ifndef __GNUC__
# define __attribute__(x)
#endif
//////////////////////////////////////////////////////////////////////
#ifdef ATTRIBUTE_UNUSED
# undef ATTRIBUTE_UNUSED
#endif
#ifdef ATTRIBUTE_NORETURN
# undef ATTRIBUTE_NORETURN
#endif
#ifdef ATTRIBUTE_PRINTF
# undef ATTRIBUTE_PRINTF
#endif
#ifdef ATTRIBUTE_PRINTF_STRING
# undef ATTRIBUTE_PRINTF_STRING
#endif
#define ATTRIBUTE_PRINTF_STRING FOLLY_PRINTF_FORMAT
#ifdef _MSC_VER
#define ATTRIBUTE_NORETURN __declspec(noreturn)
#define ATTRIBUTE_PRINTF(a1, a2)
#ifndef __thread
# define __thread __declspec(thread)
#endif
#define ATTRIBUTE_UNUSED
#define ALWAYS_INLINE __forceinline
#define EXTERNALLY_VISIBLE
#define FLATTEN
#define NEVER_INLINE __declspec(noinline)
#define UNUSED
#else
#define ATTRIBUTE_NORETURN __attribute__((__noreturn__))
#define ATTRIBUTE_PRINTF(a1, a2) \
__attribute__((__format__ (__printf__, a1, a2)))
#define ATTRIBUTE_UNUSED __attribute__((__unused__))
#define ALWAYS_INLINE inline __attribute__((__always_inline__))
#define EXTERNALLY_VISIBLE __attribute__((__externally_visible__))
#define FLATTEN __attribute__((__flatten__))
#define NEVER_INLINE __attribute__((__noinline__))
#define UNUSED __attribute__((__unused__))
#endif
#ifdef DEBUG
# define DEBUG_ONLY /* nop */
#else
# define DEBUG_ONLY UNUSED
#endif
/*
* We need to keep some unreferenced functions from being removed by
* the linker. There is no compile time mechanism for doing this, but
* by putting them in the same section as some other, referenced function
* in the same file, we can keep them around.
*
* So this macro should be used to mark at least one function that is
* referenced, and other functions that are not referenced in the same
* file.
*
* Note: this may not work properly with LTO. We'll revisit when/if we
* move to it.
*/
#ifndef __APPLE__
# define KEEP_SECTION \
__attribute__((__section__(".text.keep")))
#else
# define KEEP_SECTION \
__attribute__((__section__(".text,.text.keep")))
#endif
#if defined(__APPLE__)
// OS X has a macro "isset" defined in this header. Force the include so we can
// make sure the macro gets undef'd. (I think this also applies to BSD, but we
// can cross that road when we come to it.)
# include <sys/param.h>
# ifdef isset
# undef isset
# endif
#endif
//////////////////////////////////////////////////////////////////////
#if defined(__x86_64__)
# if defined(__clang__)
# define DECLARE_FRAME_POINTER(fp) \
ActRec* fp; \
asm volatile("mov %%rbp, %0" : "=r" (fp) ::)
# else
# define DECLARE_FRAME_POINTER(fp) register ActRec* fp asm("rbp");
# endif
#elif defined(_M_X64)
// TODO: FIXME! Without this implemented properly, the JIT
// will fail "pretty spectacularly".
# define DECLARE_FRAME_POINTER(fp) \
always_assert(false); \
register ActRec* fp = nullptr;
#elif defined(__AARCH64EL__)
# if defined(__clang__)
# error Clang implementation not done for ARM
# endif
# define DECLARE_FRAME_POINTER(fp) register ActRec* fp asm("x29");
#elif defined(__powerpc64__)
# if defined(__clang__)
# error Clang implementation not done for PPC64
# endif
# define DECLARE_FRAME_POINTER(fp) register ActRec* fp = (ActRec*) __builtin_frame_address(0);
#else
# error What are the stack and frame pointers called on your architecture?
#endif
//////////////////////////////////////////////////////////////////////
// We reserve the exit status 127 to signal a failure in the
// interpreter. 127 is a valid exit code on all reasonable
// architectures: POSIX requires at least 8 unsigned bits and
// Windows 32 signed bits.
#define HPHP_EXIT_FAILURE 127
//////////////////////////////////////////////////////////////////////
#if FACEBOOK
// Linking in libbfd is a gigantic PITA. If you want this yourself in a non-FB
// build, feel free to define HAVE_LIBBFD and specify the right options to link
// in libbfd.a in the extra C++ options.
#define HAVE_LIBBFD 1
#endif
#ifndef PACKAGE
// The value doesn't matter, but it must be defined before you include
// bfd.h
#define PACKAGE "hhvm"
#endif
//////////////////////////////////////////////////////////////////////
#endif

View File

@@ -1,99 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "thread-local.h"
#ifdef __linux__
#include <link.h>
#include <asm/prctl.h>
#include <sys/prctl.h>
extern "C" {
extern int arch_prctl(int, unsigned long*);
}
#endif //__linux__
namespace HPHP {
#ifdef USE_GCC_FAST_TLS
void ThreadLocalManager::OnThreadExit(void* p) {
auto list = getList(p);
p = list->head;
delete list;
while (p != nullptr) {
auto* pNode = static_cast<ThreadLocalNode<void>*>(p);
if (pNode->m_on_thread_exit_fn) {
pNode->m_on_thread_exit_fn(p);
}
p = pNode->m_next;
}
}
void ThreadLocalManager::PushTop(void* nodePtr, size_t nodeSize) {
auto& node = *static_cast<ThreadLocalNode<void>*>(nodePtr);
auto key = GetManager().m_key;
auto list = getList(pthread_getspecific(key));
if (UNLIKELY(!list)) {
ThreadLocalSetValue(key, list = new ThreadLocalList);
}
node.m_next = list->head;
node.m_size = nodeSize;
list->head = node.m_next;
}
ThreadLocalManager& ThreadLocalManager::GetManager() {
static ThreadLocalManager m;
return m;
}
#ifdef __APPLE__
ThreadLocalManager::ThreadLocalList::ThreadLocalList() {
pthread_t self = pthread_self();
handler.__routine = ThreadLocalManager::OnThreadExit;
handler.__arg = this;
handler.__next = self->__cleanup_stack;
self->__cleanup_stack = &handler;
}
#endif
#endif
#ifdef __linux__
static int visit_phdr(dl_phdr_info* info, size_t, void*) {
for (size_t i = 0, n = info->dlpi_phnum; i < n; ++i) {
const auto& hdr = info->dlpi_phdr[i];
auto addr = info->dlpi_addr + hdr.p_vaddr;
if (addr < 0x100000000LL && hdr.p_type == PT_TLS) {
// found the main thread-local section
assert(int(hdr.p_memsz) == hdr.p_memsz); // ensure no truncation
return hdr.p_memsz;
}
}
return 0;
}
std::pair<void*,size_t> getCppTdata() {
uintptr_t addr;
if (!arch_prctl(ARCH_GET_FS, &addr)) {
// fs points to the end of the threadlocal area.
size_t size = dl_iterate_phdr(&visit_phdr, nullptr);
return {(void*)(addr - size), size};
}
return {nullptr, 0};
}
#else
// how do you find the thread local section on your system?
std::pair<void*,size_t> getCppTdata() {
return {nullptr, 0};
}
#endif //__linux__
}

View File

@@ -1,760 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#ifndef incl_HPHP_THREAD_LOCAL_H_
#define incl_HPHP_THREAD_LOCAL_H_
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <pthread.h>
#include <errno.h>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "portability.h"
namespace HPHP {
// return the location of the current thread's tdata section
std::pair<void*,size_t> getCppTdata();
inline uintptr_t tlsBase() {
uintptr_t retval;
#if defined(__x86_64__)
asm ("movq %%fs:0, %0" : "=r" (retval));
#elif defined(__AARCH64EL__)
// mrs == "move register <-- system"
// tpidr_el0 == "thread process id register for exception level 0"
asm ("mrs %0, tpidr_el0" : "=r" (retval));
#elif defined (__powerpc64__)
asm ("xor %0,%0,%0\n\t"
"or %0,%0,13\n\t"
: "=r" (retval));
#elif defined(_M_X64)
retval = (uintptr_t)_readfsbase_u64();
retval = *(uintptr_t*)(retval + 88);
#else
# error How do you access thread-local storage on this machine?
#endif
return retval;
}
///////////////////////////////////////////////////////////////////////////////
// gcc >= 4.3.0 supports the '__thread' keyword for thread locals
//
// Clang seems to have added this feature, or at the very least it is ignoring
// __thread keyword and compiling anyway
//
// On OSX, gcc does emulate TLS but in a manner that invalidates assumptions
// we have made about __thread and makes accessing thread-local variables in a
// JIT-friendly fashion difficult (as the compiler is doing a lot of magic that
// is not contractual or documented that we would need to duplicate in emitted
// code) so for now we're not going to use it. One possibility if we really
// want to do this is to generate functions that access variables of interest
// in ThreadLocal* (all of them are NoCheck right now) and use the bytes of
// gcc's compiled functions to find the values we would need to pass to
// __emutls_get_address.
//
// icc 13.0.0 appears to support it as well but we end up with
// assembler warnings of unknown importance about incorrect section
// types
//
// __thread on cygwin and mingw uses pthreads emulation not native tls so
// the emulation for thread local must be used as well
//
// So we use __thread on gcc, icc and clang, unless we are on OSX. On OSX, we
// use our own emulation. Use the DECLARE_THREAD_LOCAL() and
// IMPLEMENT_THREAD_LOCAL() macros to access either __thread or the emulation
// as appropriate.
#if !defined(NO_TLS) && \
!defined(__CYGWIN__) && !defined(__MINGW__) && \
((__llvm__ && __clang__) || \
__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3) || \
__INTEL_COMPILER || defined(_MSC_VER))
#define USE_GCC_FAST_TLS
#endif
///////////////////////////////////////////////////////////////////////////////
// helper
inline void ThreadLocalCheckReturn(int ret, const char *funcName) {
if (ret != 0) {
// This is used from global constructors so the safest thing to do is just
// print to stderr and exit().
fprintf(stderr, "%s returned %d", funcName, ret);
exit(1);
}
}
inline void ThreadLocalCreateKey(pthread_key_t *key, void (*del)(void*)) {
int ret = pthread_key_create(key, del);
ThreadLocalCheckReturn(ret, "pthread_key_create");
}
inline void ThreadLocalSetValue(pthread_key_t key, const void* value) {
int ret = pthread_setspecific(key, value);
ThreadLocalCheckReturn(ret, "pthread_setspecific");
}
#ifdef __APPLE__
typedef struct __darwin_pthread_handler_rec darwin_pthread_handler;
#endif
///////////////////////////////////////////////////////////////////////////////
/**
* A thread-local object is a "global" object within a thread. This is useful
* for writing apartment-threaded code, where nothing is actually shared
* between different threads (hence no locking) but those variables are not
* on stack in local scope. To use it, just do something like this,
*
* IMPLEMENT_THREAD_LOCAL(MyClass, static_object);
* static_object->data_ = ...;
* static_object->doSomething();
*
* IMPLEMENT_THREAD_LOCAL(int, static_number);
* int value = *static_number;
*
* So, syntax-wise it's similar to pointers. The type parameter can be a
* primitive types. If it's a class, there has to be a default constructor.
*/
///////////////////////////////////////////////////////////////////////////////
#if defined(USE_GCC_FAST_TLS)
/**
* We keep a linked list of destructors in ThreadLocalManager to be called on
* thread exit. ThreadLocalNode is a node in this list.
*/
template <typename T>
struct ThreadLocalNode {
T * m_p;
void (*m_on_thread_exit_fn)(void * p);
void * m_next;
size_t m_size;
};
struct ThreadLocalManager {
template<class T>
static void PushTop(ThreadLocalNode<T>& node) {
PushTop(&node, sizeof(T));
}
template<class F> void scan(F& mark) const;
private:
static void PushTop(void* node, size_t size);
struct ThreadLocalList {
void* head{nullptr};
#ifdef __APPLE__
ThreadLocalList();
darwin_pthread_handler handler;
#endif
};
static ThreadLocalList* getList(void* p) {
return static_cast<ThreadLocalList*>(p);
}
ThreadLocalManager() : m_key(0) {
#ifdef __APPLE__
ThreadLocalCreateKey(&m_key, nullptr);
#else
ThreadLocalCreateKey(&m_key, ThreadLocalManager::OnThreadExit);
#endif
};
static void OnThreadExit(void *p);
pthread_key_t m_key;
static ThreadLocalManager& GetManager();
};
///////////////////////////////////////////////////////////////////////////////
// ThreadLocal allocates by calling new without parameters and frees by calling
// delete
template<typename T>
void ThreadLocalOnThreadExit(void * p) {
ThreadLocalNode<T> * pNode = (ThreadLocalNode<T>*)p;
delete pNode->m_p;
pNode->m_p = nullptr;
}
/**
* The USE_GCC_FAST_TLS implementation of ThreadLocal is just a lazy-initialized
* pointer wrapper. In this case, we have one ThreadLocal object per thread.
*/
template<typename T>
struct ThreadLocal {
T *get() const {
if (m_node.m_p == nullptr) {
const_cast<ThreadLocal<T>*>(this)->create();
}
return m_node.m_p;
}
NEVER_INLINE void create();
bool isNull() const { return m_node.m_p == nullptr; }
void destroy() {
delete m_node.m_p;
m_node.m_p = nullptr;
}
void nullOut() {
m_node.m_p = nullptr;
}
T *operator->() const {
return get();
}
T &operator*() const {
return *get();
}
ThreadLocalNode<T> m_node;
};
template<typename T>
void ThreadLocal<T>::create() {
if (m_node.m_on_thread_exit_fn == nullptr) {
m_node.m_on_thread_exit_fn = ThreadLocalOnThreadExit<T>;
ThreadLocalManager::PushTop(m_node);
}
assert(m_node.m_p == nullptr);
m_node.m_p = new T();
}
/**
* ThreadLocalNoCheck is a pointer wrapper like ThreadLocal, except that it is
* explicitly initialized with getCheck(), rather than being initialized when
* it is first dereferenced.
*/
template<typename T>
struct ThreadLocalNoCheck {
NEVER_INLINE T *getCheck() const;
T* getNoCheck() const {
assert(m_node.m_p);
return m_node.m_p;
}
NEVER_INLINE void create();
bool isNull() const { return m_node.m_p == nullptr; }
void destroy() {
delete m_node.m_p;
m_node.m_p = nullptr;
}
T *operator->() const {
return getNoCheck();
}
T &operator*() const {
return *getNoCheck();
}
ThreadLocalNode<T> m_node;
private:
void setNull() { m_node.m_p = nullptr; }
};
template<typename T>
void ThreadLocalNoCheck<T>::create() {
if (m_node.m_on_thread_exit_fn == nullptr) {
m_node.m_on_thread_exit_fn = ThreadLocalOnThreadExit<T>;
ThreadLocalManager::PushTop(m_node);
}
assert(m_node.m_p == nullptr);
m_node.m_p = new T();
}
template<typename T>
T *ThreadLocalNoCheck<T>::getCheck() const {
if (m_node.m_p == nullptr) {
const_cast<ThreadLocalNoCheck<T>*>(this)->create();
}
return m_node.m_p;
}
///////////////////////////////////////////////////////////////////////////////
// Singleton thread-local storage for T
template<typename T>
void ThreadLocalSingletonOnThreadExit(void *obj) {
T::OnThreadExit((T*)obj);
}
// ThreadLocalSingleton has NoCheck property
template <typename T>
class ThreadLocalSingleton {
public:
ThreadLocalSingleton() { s_inited = true; }
NEVER_INLINE static T *getCheck();
static T* getNoCheck() {
assert(s_inited);
assert(s_singleton == (T*)&s_storage);
return (T*)&s_storage;
}
static bool isNull() { return s_singleton == nullptr; }
static void destroy() {
assert(!s_singleton || s_singleton == (T*)&s_storage);
T* p = s_singleton;
if (p) {
T::Delete(p);
s_singleton = nullptr;
}
}
T *operator->() const {
return getNoCheck();
}
T &operator*() const {
return *getNoCheck();
}
private:
static __thread T *s_singleton;
typedef typename std::aligned_storage<sizeof(T), sizeof(void*)>::type
StorageType;
static __thread StorageType s_storage;
static bool s_inited; // no-fast-TLS requires construction so be consistent
};
template<typename T>
bool ThreadLocalSingleton<T>::s_inited = false;
template<typename T>
T *ThreadLocalSingleton<T>::getCheck() {
assert(s_inited);
if (!s_singleton) {
T* p = (T*) &s_storage;
T::Create(p);
s_singleton = p;
}
return s_singleton;
}
template<typename T> __thread T *ThreadLocalSingleton<T>::s_singleton;
template<typename T> __thread typename ThreadLocalSingleton<T>::StorageType
ThreadLocalSingleton<T>::s_storage;
///////////////////////////////////////////////////////////////////////////////
// some classes don't need new/delete at all
template<typename T, bool throwOnNull = true>
struct ThreadLocalProxy {
T *get() const {
if (m_p == nullptr && throwOnNull) {
throw std::runtime_error("ThreadLocalProxy::get() called before set()");
}
return m_p;
}
void set(T* obj) {
m_p = obj;
}
bool isNull() const { return m_p == nullptr; }
void destroy() {
m_p = nullptr;
}
T *operator->() const {
return get();
}
T &operator*() const {
return *get();
}
T * m_p;
};
/*
* How to use the thread-local macros:
*
* Use DECLARE_THREAD_LOCAL to declare a *static* class field as thread local:
* class SomeClass {
* static DECLARE_THREAD_LOCAL(SomeFieldType, f);
* }
*
* Use IMPLEMENT_THREAD_LOCAL in the cpp file to implement the field:
* IMPLEMENT_THREAD_LOCAL(SomeFieldType, SomeClass::f);
*
* Remember: *Never* write IMPLEMENT_THREAD_LOCAL in a header file.
*/
#define DECLARE_THREAD_LOCAL(T, f) \
__thread HPHP::ThreadLocal<T> f
#define IMPLEMENT_THREAD_LOCAL(T, f) \
__thread HPHP::ThreadLocal<T> f
#define DECLARE_THREAD_LOCAL_NO_CHECK(T, f) \
__thread HPHP::ThreadLocalNoCheck<T> f
#define IMPLEMENT_THREAD_LOCAL_NO_CHECK(T, f) \
__thread HPHP::ThreadLocalNoCheck<T> f
#define DECLARE_THREAD_LOCAL_PROXY(T, N, f) \
__thread HPHP::ThreadLocalProxy<T, N> f
#define IMPLEMENT_THREAD_LOCAL_PROXY(T, N, f) \
__thread HPHP::ThreadLocalProxy<T, N> f
#else /* USE_GCC_FAST_TLS */
///////////////////////////////////////////////////////////////////////////////
// ThreadLocal allocates by calling new() without parameters
template<typename T>
void ThreadLocalOnThreadExit(void *p) {
delete (T*)p;
}
#ifdef __APPLE__
// The __thread variables in class T will be freed when pthread calls
// the destructor function on Mac. We can register a handler in
// pthread_t->__cleanup_stack similar to pthread_cleanup_push(). The handler
// will be called earlier so the __thread variables will still exist in the
// handler when the thread exits.
//
// See the details at:
// https://github.com/facebook/hhvm/issues/4444#issuecomment-92497582
typedef struct __darwin_pthread_handler_rec darwin_pthread_handler;
template<typename T>
void ThreadLocalOnThreadCleanup(void *key) {
void *obj = pthread_getspecific((pthread_key_t)key);
if (obj) {
ThreadLocalOnThreadExit<T>(obj);
}
}
inline void ThreadLocalSetCleanupHandler(pthread_key_t cleanup_key,
pthread_key_t key,
void (*del)(void*)) {
// Prevent from adding the handler for multiple times.
darwin_pthread_handler *handler =
(darwin_pthread_handler*)pthread_getspecific(cleanup_key);
if (handler)
return;
pthread_t self = pthread_self();
handler = new darwin_pthread_handler();
handler->__routine = del;
handler->__arg = (void*)key;
handler->__next = self->__cleanup_stack;
self->__cleanup_stack = handler;
ThreadLocalSetValue(cleanup_key, handler);
}
#endif
/**
* This is the emulation version of ThreadLocal. In this case, the ThreadLocal
* object is a true global, and the get() method returns a thread-dependent
* pointer from pthread's thread-specific data management.
*/
template<typename T>
class ThreadLocal {
public:
/**
* Constructor that has to be called from a thread-neutral place.
*/
ThreadLocal() : m_key(0) {
#ifdef __APPLE__
ThreadLocalCreateKey(&m_key, nullptr);
ThreadLocalCreateKey(&m_cleanup_key,
ThreadLocalOnThreadExit<darwin_pthread_handler>);
#else
ThreadLocalCreateKey(&m_key, ThreadLocalOnThreadExit<T>);
#endif
}
T *get() const {
T *obj = (T*)pthread_getspecific(m_key);
if (obj == nullptr) {
obj = new T();
ThreadLocalSetValue(m_key, obj);
#ifdef __APPLE__
ThreadLocalSetCleanupHandler(m_cleanup_key, m_key,
ThreadLocalOnThreadCleanup<T>);
#endif
}
return obj;
}
bool isNull() const { return pthread_getspecific(m_key) == nullptr; }
void destroy() {
delete (T*)pthread_getspecific(m_key);
ThreadLocalSetValue(m_key, nullptr);
}
void nullOut() {
ThreadLocalSetValue(m_key, nullptr);
}
/**
* Access object's member or method through this operator overload.
*/
T *operator->() const {
return get();
}
T &operator*() const {
return *get();
}
private:
pthread_key_t m_key;
#ifdef __APPLE__
pthread_key_t m_cleanup_key;
#endif
};
template<typename T>
class ThreadLocalNoCheck {
public:
/**
* Constructor that has to be called from a thread-neutral place.
*/
ThreadLocalNoCheck() : m_key(0) {
#ifdef __APPLE__
ThreadLocalCreateKey(&m_key, nullptr);
ThreadLocalCreateKey(&m_cleanup_key,
ThreadLocalOnThreadExit<darwin_pthread_handler>);
#else
ThreadLocalCreateKey(&m_key, ThreadLocalOnThreadExit<T>);
#endif
}
NEVER_INLINE T *getCheck() const;
T* getNoCheck() const {
T *obj = (T*)pthread_getspecific(m_key);
assert(obj);
return obj;
}
bool isNull() const { return pthread_getspecific(m_key) == nullptr; }
void destroy() {
delete (T*)pthread_getspecific(m_key);
ThreadLocalSetValue(m_key, nullptr);
}
/**
* Access object's member or method through this operator overload.
*/
T *operator->() const {
return getNoCheck();
}
T &operator*() const {
return *getNoCheck();
}
public:
void setNull() { ThreadLocalSetValue(m_key, nullptr); }
pthread_key_t m_key;
#ifdef __APPLE__
pthread_key_t m_cleanup_key;
#endif
};
template<typename T>
T *ThreadLocalNoCheck<T>::getCheck() const {
T *obj = (T*)pthread_getspecific(m_key);
if (obj == nullptr) {
obj = new T();
ThreadLocalSetValue(m_key, obj);
#ifdef __APPLE__
ThreadLocalSetCleanupHandler(m_cleanup_key, m_key,
ThreadLocalOnThreadCleanup<T>);
#endif
}
return obj;
}
///////////////////////////////////////////////////////////////////////////////
// Singleton thread-local storage for T
template<typename T>
void ThreadLocalSingletonOnThreadExit(void *obj) {
T::OnThreadExit((T*)obj);
free(obj);
}
#ifdef __APPLE__
template<typename T>
void ThreadLocalSingletonOnThreadCleanup(void *key) {
void *obj = pthread_getspecific((pthread_key_t)key);
if (obj) {
ThreadLocalSingletonOnThreadExit<T>(obj);
}
}
#endif
// ThreadLocalSingleton has NoCheck property
template<typename T>
class ThreadLocalSingleton {
public:
ThreadLocalSingleton() { getKey(); }
NEVER_INLINE static T *getCheck();
static T* getNoCheck() {
assert(s_inited);
T *obj = (T*)pthread_getspecific(s_key);
assert(obj);
return obj;
}
static bool isNull() {
return !s_inited || pthread_getspecific(s_key) == nullptr;
}
static void destroy() {
void* p = pthread_getspecific(s_key);
T::Delete((T*)p);
free(p);
ThreadLocalSetValue(s_key, nullptr);
}
T *operator->() const {
return getNoCheck();
}
T &operator*() const {
return *getNoCheck();
}
private:
static pthread_key_t s_key;
static bool s_inited; // pthread_key_t has no portable valid sentinel
#ifdef __APPLE__
static pthread_key_t s_cleanup_key;
#endif
static pthread_key_t getKey() {
if (!s_inited) {
s_inited = true;
#ifdef __APPLE__
ThreadLocalCreateKey(&s_key, nullptr);
ThreadLocalCreateKey(&s_cleanup_key,
ThreadLocalOnThreadExit<darwin_pthread_handler>);
#else
ThreadLocalCreateKey(&s_key, ThreadLocalSingletonOnThreadExit<T>);
#endif
}
return s_key;
}
};
template<typename T>
T *ThreadLocalSingleton<T>::getCheck() {
assert(s_inited);
T *obj = (T*)pthread_getspecific(s_key);
if (obj == nullptr) {
obj = (T*)malloc(sizeof(T));
T::Create(obj);
ThreadLocalSetValue(s_key, obj);
#ifdef __APPLE__
ThreadLocalSetCleanupHandler(s_cleanup_key, s_key,
ThreadLocalSingletonOnThreadCleanup<T>);
#endif
}
return obj;
}
template<typename T>
pthread_key_t ThreadLocalSingleton<T>::s_key;
template<typename T>
bool ThreadLocalSingleton<T>::s_inited = false;
#ifdef __APPLE__
template<typename T>
pthread_key_t ThreadLocalSingleton<T>::s_cleanup_key;
#endif
///////////////////////////////////////////////////////////////////////////////
// some classes don't need new/delete at all
template<typename T, bool throwOnNull = true>
class ThreadLocalProxy {
public:
/**
* Constructor that has to be called from a thread-neutral place.
*/
ThreadLocalProxy() : m_key(0) {
ThreadLocalCreateKey(&m_key, nullptr);
}
T *get() const {
T *obj = (T*)pthread_getspecific(m_key);
if (obj == nullptr && throwOnNull) {
throw std::runtime_error("ThreadLocalProxy::get() called before set()");
}
return obj;
}
void set(T* obj) {
ThreadLocalSetValue(m_key, obj);
}
bool isNull() const { return pthread_getspecific(m_key) == nullptr; }
void destroy() {
ThreadLocalSetValue(m_key, nullptr);
}
/**
* Access object's member or method through this operator overload.
*/
T *operator->() const {
return get();
}
T &operator*() const {
return *get();
}
public:
pthread_key_t m_key;
};
/**
* The emulation version of the thread-local macros
*/
#define DECLARE_THREAD_LOCAL(T, f) HPHP::ThreadLocal<T> f
#define IMPLEMENT_THREAD_LOCAL(T, f) HPHP::ThreadLocal<T> f
#define DECLARE_THREAD_LOCAL_NO_CHECK(T, f) HPHP::ThreadLocalNoCheck<T> f
#define IMPLEMENT_THREAD_LOCAL_NO_CHECK(T, f) HPHP::ThreadLocalNoCheck<T> f
#define DECLARE_THREAD_LOCAL_PROXY(T, N, f) HPHP::ThreadLocalProxy<T, N> f
#define IMPLEMENT_THREAD_LOCAL_PROXY(T, N, f) HPHP::ThreadLocalProxy<T, N> f
#endif /* USE_GCC_FAST_TLS */
///////////////////////////////////////////////////////////////////////////////
}
#endif // incl_HPHP_THREAD_LOCAL_H_