-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
c0fff8e
commit 7dbdf4b
Showing
9 changed files
with
303 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
#ifndef COLLECTOR_PROFILER_H | ||
#define COLLECTOR_PROFILER_H | ||
|
||
#include <memory> | ||
#include <string> | ||
|
||
struct Free { | ||
void operator()(const void* p) const { free((void*)p); } | ||
}; | ||
using MallocUniquePtr = std::unique_ptr<char, Free>; | ||
|
||
#ifdef COLLECTOR_PROFILING | ||
# include "gperftools/heap-profiler.h" | ||
# include "gperftools/profiler.h" | ||
|
||
namespace collector { | ||
class Profiler { | ||
public: | ||
static inline void RegisterCPUThread() { ProfilerRegisterThread(); } | ||
static inline MallocUniquePtr AllocAndGetHeapProfile() { | ||
if (!IsHeapProfilerEnabled()) { | ||
return nullptr; | ||
} | ||
return MallocUniquePtr(GetHeapProfile()); | ||
} | ||
static inline bool StartCPUProfiler(const std::string& output_dir) { | ||
return ProfilerStart(output_dir.c_str()); | ||
} | ||
static inline void StopCPUProfiler() { | ||
ProfilerFlush(); | ||
ProfilerStop(); | ||
} | ||
static inline void StartHeapProfiler() { HeapProfilerStart(""); } | ||
static inline void StopHeapProfiler() { | ||
if (!IsHeapProfilerEnabled()) { | ||
return; | ||
} | ||
HeapProfilerStop(); | ||
} | ||
static inline bool IsCPUProfilerEnabled() { return ProfilingIsEnabledForAllThreads(); } | ||
static inline bool IsCPUProfilerSupported() { return true; } | ||
static inline bool IsHeapProfilerEnabled() { return IsHeapProfilerRunning(); } | ||
static inline bool IsHeapProfilerSupported() { return true; } | ||
}; | ||
|
||
} // namespace collector | ||
#else | ||
|
||
namespace collector { | ||
class Profiler { | ||
public: | ||
static inline void RegisterCPUThread() {} | ||
static inline MallocUniquePtr AllocAndGetHeapProfile() { return MallocUniquePtr(nullptr); } | ||
static inline bool StartCPUProfiler(const std::string& output_dir) { return false; } | ||
static inline void StopCPUProfiler() {} | ||
static inline void StartHeapProfiler() {} | ||
static inline void StopHeapProfiler() {} | ||
static inline bool IsCPUProfilerSupported() { return false; } | ||
static inline bool IsCPUProfilerEnabled() { return false; } | ||
static inline bool IsHeapProfilerSupported() { return false; } | ||
static inline bool IsHeapProfilerEnabled() { return false; } | ||
}; | ||
} // namespace collector | ||
#endif | ||
|
||
#endif //COLLECTOR_PROFILER_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
#include "ProfilerHandler.h" | ||
|
||
#include <algorithm> | ||
#include <cstdio> | ||
#include <cstring> | ||
|
||
#include <json/json.h> | ||
#include <sys/stat.h> | ||
|
||
#include "Logging.h" | ||
#include "Profiler.h" | ||
#include "Utility.h" | ||
|
||
namespace collector { | ||
|
||
const std::string ProfilerHandler::kCPUProfileFilename = "/module/cpu_profile"; | ||
const std::string ProfilerHandler::kBaseRoute = "/profile"; | ||
const std::string ProfilerHandler::kCPURoute = kBaseRoute + "/cpu"; | ||
const std::string ProfilerHandler::kHeapRoute = kBaseRoute + "/heap"; | ||
|
||
bool ProfilerHandler::ServerError(struct mg_connection* conn, const char* err) { | ||
return mg_send_http_error(conn, 500, err) >= 0; | ||
} | ||
|
||
bool ProfilerHandler::ClientError(struct mg_connection* conn, const char* err) { | ||
return mg_send_http_error(conn, 400, err) >= 0; | ||
} | ||
|
||
bool ProfilerHandler::SendCPUProfile(struct mg_connection* conn) { | ||
WITH_LOCK(mutex_) { | ||
if (cpu_profile_length_ == 0) { | ||
return mg_send_http_ok(conn, "text/plain", 0) >= 0; | ||
} | ||
mg_send_mime_file(conn, kCPUProfileFilename.c_str(), "application/octet-stream"); | ||
} | ||
return true; | ||
} | ||
|
||
bool ProfilerHandler::SendHeapProfile(struct mg_connection* conn) { | ||
WITH_LOCK(mutex_) { | ||
if (heap_profile_length_ == 0) { | ||
return mg_send_http_ok(conn, "text/plain", 0) >= 0; | ||
} | ||
if (mg_send_http_ok(conn, "application/octet-stream", heap_profile_length_) < 0) { | ||
return false; | ||
} | ||
if (mg_write(conn, heap_profile_.get(), heap_profile_length_) != heap_profile_length_) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
bool ProfilerHandler::SendStatus(struct mg_connection* conn) { | ||
Json::Value resp(Json::objectValue); | ||
WITH_LOCK(mutex_) { | ||
resp["supports_cpu"] = Profiler::IsCPUProfilerSupported(); | ||
resp["supports_heap"] = Profiler::IsHeapProfilerSupported(); | ||
resp["cpu"] = Profiler::IsCPUProfilerEnabled() ? "on" : (cpu_profile_length_ > 0 ? "off" : "empty"); | ||
resp["heap"] = Profiler::IsHeapProfilerEnabled() ? "on" : (heap_profile_length_ > 0 ? "off" : "empty"); | ||
} | ||
std::string json_body = resp.toStyledString(); | ||
if (mg_send_http_ok(conn, "application/json", json_body.length()) < 0) { | ||
return false; | ||
} | ||
return mg_write(conn, json_body.c_str(), json_body.length()) >= 0; | ||
} | ||
|
||
bool ProfilerHandler::HandleCPURoute(struct mg_connection* conn, const std::string& post_data) { | ||
WITH_LOCK(mutex_) { | ||
if (post_data == "on") { | ||
if (!Profiler::IsCPUProfilerEnabled()) { | ||
if (!Profiler::StartCPUProfiler(kCPUProfileFilename)) { | ||
return ServerError(conn, "failed starting cpu profiler"); | ||
} | ||
CLOG(INFO) << "started cpu profiler"; | ||
} | ||
} else if (post_data == "off") { | ||
if (Profiler::IsCPUProfilerEnabled()) { | ||
Profiler::StopCPUProfiler(); | ||
struct stat sdata; | ||
if (stat(kCPUProfileFilename.c_str(), &sdata) == 0) { | ||
cpu_profile_length_ = sdata.st_size; | ||
} | ||
CLOG(INFO) << "stopped cpu profiler, bytes=" << cpu_profile_length_; | ||
} | ||
} else if (post_data == "empty") { | ||
if (cpu_profile_length_ != 0) { | ||
cpu_profile_length_ = 0; | ||
if (std::remove(kCPUProfileFilename.c_str()) != 0) { | ||
CLOG(INFO) << "remove failed: " << StrError(); | ||
return ServerError(conn, "failure while deleting cpu profile"); | ||
} | ||
CLOG(INFO) << "cleared cpu profile"; | ||
} | ||
} else { | ||
return ClientError(conn, "invalid post data"); | ||
} | ||
} | ||
return mg_send_http_ok(conn, "text/plain", 0) >= 0; | ||
} | ||
|
||
bool ProfilerHandler::HandleHeapRoute(struct mg_connection* conn, const std::string& post_data) { | ||
WITH_LOCK(mutex_) { | ||
if (post_data == "on") { | ||
if (!Profiler::IsHeapProfilerEnabled()) { | ||
Profiler::StartHeapProfiler(); | ||
CLOG(INFO) << "started heap profiler"; | ||
} | ||
} else if (post_data == "off") { | ||
if (Profiler::IsHeapProfilerEnabled()) { | ||
heap_profile_ = Profiler::AllocAndGetHeapProfile(); | ||
if (heap_profile_ == nullptr) { | ||
return ServerError(conn, "failed get heap profile"); | ||
} | ||
heap_profile_length_ = strlen(heap_profile_.get()); | ||
Profiler::StopHeapProfiler(); | ||
CLOG(INFO) << "stopped heap profiler, bytes=" << heap_profile_length_; | ||
} | ||
} else if (post_data == "empty") { | ||
if (heap_profile_length_ != 0) { | ||
heap_profile_length_ = 0; | ||
heap_profile_.reset(); | ||
CLOG(INFO) << "cleared heap profile"; | ||
} | ||
} else { | ||
return ClientError(conn, "invalid post data"); | ||
} | ||
} | ||
return mg_send_http_ok(conn, "text/plain", 0) >= 0; | ||
} | ||
|
||
bool ProfilerHandler::handlePost(CivetServer* server, struct mg_connection* conn) { | ||
if (!Profiler::IsCPUProfilerSupported()) { | ||
return ServerError(conn, "not supported"); | ||
} | ||
const mg_request_info* req_info = mg_get_request_info(conn); | ||
if (req_info == nullptr) { | ||
return ServerError(conn, "unable to read request"); | ||
} | ||
std::string uri(req_info->local_uri); | ||
std::string post_data(server->getPostData(conn)); | ||
if (uri == kCPURoute) { | ||
return HandleCPURoute(conn, post_data); | ||
} else if (uri == kHeapRoute) { | ||
return HandleHeapRoute(conn, post_data); | ||
} | ||
return ClientError(conn, "unknown route"); | ||
} | ||
|
||
bool ProfilerHandler::handleGet(CivetServer* server, struct mg_connection* conn) { | ||
const mg_request_info* req_info = mg_get_request_info(conn); | ||
if (req_info == nullptr) { | ||
return ServerError(conn, "unable to read request"); | ||
} | ||
std::string uri = req_info->local_uri; | ||
if (uri == kBaseRoute) { | ||
return SendStatus(conn); | ||
} else if (uri == kHeapRoute) { | ||
return SendHeapProfile((conn)); | ||
} else if (uri == kCPURoute) { | ||
return SendCPUProfile(conn); | ||
} | ||
return ClientError(conn, "unknown route"); | ||
} | ||
|
||
} // namespace collector |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
#ifndef COLLECTOR_PROFILERHANDLER_H | ||
#define COLLECTOR_PROFILERHANDLER_H | ||
|
||
#include <memory> | ||
#include <mutex> | ||
|
||
#include "Profiler.h" | ||
#include "civetweb/CivetServer.h" | ||
|
||
namespace collector { | ||
|
||
// Profiling Endpoints: | ||
// POST /profile/cpu | ||
// - accepts post data of on|off|empty to enable, disable or clear/delete the latest cpu profile | ||
// POST /profile/heap | ||
// - accepts post data of on|off|empty to enable, disable or clear/delete the latest heap profile | ||
// GET /profile | ||
// - get profiling support and status | ||
// GET /profile/cpu | ||
// - get latest cpu profile | ||
// GET /profile/heap | ||
// - get latest heap profile | ||
class ProfilerHandler : public CivetHandler { | ||
public: | ||
static const std::string kCPUProfileFilename; | ||
static const std::string kBaseRoute; | ||
static const std::string kCPURoute; | ||
static const std::string kHeapRoute; | ||
|
||
bool handleGet(CivetServer* server, struct mg_connection* conn); | ||
bool handlePost(CivetServer* server, struct mg_connection* conn); | ||
|
||
private: | ||
bool ServerError(struct mg_connection* conn, const char* err); | ||
bool ClientError(struct mg_connection* conn, const char* err); | ||
bool SendStatus(struct mg_connection* conn); | ||
bool SendHeapProfile(struct mg_connection* conn); | ||
bool SendCPUProfile(struct mg_connection* conn); | ||
bool HandleCPURoute(struct mg_connection* conn, const std::string& post_data); | ||
bool HandleHeapRoute(struct mg_connection* conn, const std::string& post_data); | ||
MallocUniquePtr heap_profile_; | ||
size_t heap_profile_length_; | ||
size_t cpu_profile_length_; | ||
std::mutex mutex_; | ||
}; | ||
} // namespace collector | ||
|
||
#endif //COLLECTOR_PROFILERHANDLER_H |