This source file includes following definitions.
- header
- GetLwp
- pthread_self_
- GetInstance
- LogEnter
- AtForkPrepare
- AtForkParent
- AtForkChild
- StartFlushLogThread
- AddNewLog
- WriteLogLine
- FlushLog
- FlushLogThread
- __cyg_profile_func_enter
- __cyg_profile_func_exit
#include <fcntl.h>
#include <fstream>
#include <pthread.h>
#include <stdarg.h>
#include <string>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <vector>
#include "base/containers/hash_tables.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/synchronization/lock.h"
namespace cygprofile {
extern "C" {
void __cyg_profile_func_enter(void* this_fn, void* call_site)
__attribute__((no_instrument_function));
void __cyg_profile_func_exit(void* this_fn, void* call_site)
__attribute__((no_instrument_function));
}
struct CygLogEntry {
time_t seconds;
long int usec;
pid_t pid;
pthread_t tid;
const void* this_fn;
CygLogEntry(time_t seconds, long int usec,
pid_t pid, pthread_t tid, const void* this_fn)
: seconds(seconds), usec(usec),
pid(pid), tid(tid), this_fn(this_fn) {}
};
class CygCommon {
public:
static CygCommon* GetInstance();
std::string header() const { return header_line_; }
private:
CygCommon();
std::string header_line_;
friend struct DefaultSingletonTraits<CygCommon>;
DISALLOW_COPY_AND_ASSIGN(CygCommon);
};
static pid_t GetLwp() {
return syscall(__NR_gettid);
}
class CygTlsLog {
public:
CygTlsLog()
: in_use_(false), lwp_(GetLwp()), pthread_self_(pthread_self()) { }
void LogEnter(void* this_fn);
static void AddNewLog(CygTlsLog* newlog);
static void StartFlushLogThread();
private:
static const int kBufMaxSize;
static const char kLogFilenameFmt[];
static const char kLogFileNamePrefix[];
void FlushLog();
static void AtForkPrepare();
static void AtForkParent();
static void AtForkChild();
static void* FlushLogThread(void*);
std::string log_filename_;
std::vector<CygLogEntry> buf_;
base::Lock log_mutex_;
bool in_use_;
std::hash_set<void*> functions_called_;
pid_t lwp_;
pthread_t pthread_self_;
DISALLOW_COPY_AND_ASSIGN(CygTlsLog);
};
struct AllLogs {
std::vector<CygTlsLog*> logs;
base::Lock mutex;
};
base::LazyInstance<AllLogs>::Leaky all_logs_ = LAZY_INSTANCE_INITIALIZER;
static __thread CygTlsLog* tls_current_log = NULL;
CygTlsLog* const kMagicBeingConstructed = reinterpret_cast<CygTlsLog*>(1);
const int CygTlsLog::kBufMaxSize = 3000;
#if defined(OS_ANDROID)
const char CygTlsLog::kLogFileNamePrefix[] =
"/data/local/tmp/chrome/cyglog/";
#else
const char CygTlsLog::kLogFileNamePrefix[] = "/var/log/chrome/";
#endif
const char CygTlsLog::kLogFilenameFmt[] = "%scyglog.%d.%d.%ld-%d";
CygCommon* CygCommon::GetInstance() {
return Singleton<CygCommon>::get();
}
CygCommon::CygCommon() {
std::ifstream mapsfile("/proc/self/maps");
CHECK(mapsfile.good());
static const int kMaxLineSize = 512;
char line[kMaxLineSize];
void (*this_fn)(void) =
reinterpret_cast<void(*)()>(__cyg_profile_func_enter);
while (mapsfile.getline(line, kMaxLineSize)) {
const std::string str_line = line;
size_t permindex = str_line.find("r-xp");
if (permindex != std::string::npos) {
int dashindex = str_line.find("-");
int spaceindex = str_line.find(" ");
char* p;
void* start = reinterpret_cast<void*>
(strtol((str_line.substr(0, dashindex)).c_str(),
&p, 16));
CHECK(*p == 0);
void* end = reinterpret_cast<void*>
(strtol((str_line.substr(dashindex + 1,
spaceindex - dashindex - 1)).c_str(),
&p, 16));
CHECK(*p == 0);
if (this_fn >= start && this_fn < end)
header_line_ = str_line;
}
}
mapsfile.close();
header_line_.append("\nsecs\tmsecs\tpid:threadid\tfunc\n");
}
void CygTlsLog::LogEnter(void* this_fn) {
if (in_use_)
return;
in_use_ = true;
if (functions_called_.find(this_fn) ==
functions_called_.end()) {
functions_called_.insert(this_fn);
base::AutoLock lock(log_mutex_);
if (buf_.capacity() < kBufMaxSize)
buf_.reserve(kBufMaxSize);
struct timeval timestamp;
gettimeofday(×tamp, NULL);
buf_.push_back(CygLogEntry(time(NULL), timestamp.tv_usec,
getpid(), pthread_self(), this_fn));
if (buf_.size() == kBufMaxSize) {
FlushLog();
}
}
in_use_ = false;
}
void CygTlsLog::AtForkPrepare() {
CHECK(tls_current_log);
CHECK(tls_current_log->lwp_ == GetLwp());
CHECK(tls_current_log->pthread_self_ == pthread_self());
all_logs_.Get().mutex.Acquire();
}
void CygTlsLog::AtForkParent() {
CHECK(tls_current_log);
CHECK(tls_current_log->lwp_ == GetLwp());
CHECK(tls_current_log->pthread_self_ == pthread_self());
all_logs_.Get().mutex.Release();
}
void CygTlsLog::AtForkChild() {
CHECK(tls_current_log);
pid_t lwp = GetLwp();
CHECK(tls_current_log->lwp_ != lwp);
tls_current_log->lwp_ = lwp;
CHECK(tls_current_log->pthread_self_ == pthread_self());
AllLogs& all_logs = all_logs_.Get();
all_logs.logs.clear();
all_logs.logs.push_back(tls_current_log);
CHECK(all_logs.logs.size() == 1);
tls_current_log->log_filename_.clear();
StartFlushLogThread();
all_logs.mutex.Release();
}
void CygTlsLog::StartFlushLogThread() {
pthread_t tid;
CHECK(!pthread_create(&tid, NULL, &CygTlsLog::FlushLogThread, NULL));
}
void CygTlsLog::AddNewLog(CygTlsLog* newlog) {
CHECK(tls_current_log == kMagicBeingConstructed);
AllLogs& all_logs = all_logs_.Get();
base::AutoLock lock(all_logs.mutex);
if (all_logs.logs.empty()) {
#if !defined(OS_ANDROID)
CHECK(!pthread_atfork(CygTlsLog::AtForkPrepare,
CygTlsLog::AtForkParent,
CygTlsLog::AtForkChild));
#endif
StartFlushLogThread();
}
all_logs.logs.push_back(newlog);
}
static void WriteLogLine(int fd, const char* fmt, ...) {
va_list arg_ptr;
va_start(arg_ptr, fmt);
char msg[160];
int len = vsnprintf(msg, sizeof(msg), fmt, arg_ptr);
int rc = write(fd, msg, (len > sizeof(msg))? sizeof(msg): len);
va_end(arg_ptr);
};
void CygTlsLog::FlushLog() {
bool first_log_write = false;
if (log_filename_.empty()) {
first_log_write = true;
char buf[80];
snprintf(buf, sizeof(buf), kLogFilenameFmt,
kLogFileNamePrefix, getpid(), lwp_, pthread_self_, getppid());
log_filename_ = buf;
unlink(log_filename_.c_str());
}
int file = open(log_filename_.c_str(), O_CREAT | O_WRONLY | O_APPEND, 00600);
CHECK(file != -1);
if (first_log_write)
WriteLogLine(file, "%s", CygCommon::GetInstance()->header().c_str());
for (int i = 0; i != buf_.size(); ++i) {
const CygLogEntry& p = buf_[i];
WriteLogLine(file, "%ld %ld\t%d:%ld\t%p\n",
p.seconds, p.usec, p.pid, p.tid, p.this_fn);
}
close(file);
buf_.clear();
}
void* CygTlsLog::FlushLogThread(void*) {
CHECK(tls_current_log == NULL);
tls_current_log = kMagicBeingConstructed;
while (true) {
for(int secs_to_sleep = 30; secs_to_sleep != 0;)
secs_to_sleep = sleep(secs_to_sleep);
AllLogs& all_logs = all_logs_.Get();
base::AutoLock lock(all_logs.mutex);
for (int i = 0; i != all_logs.logs.size(); ++i) {
CygTlsLog* current_log = all_logs.logs[i];
base::AutoLock current_lock(current_log->log_mutex_);
if (current_log->buf_.size()) {
current_log->FlushLog();
} else {
current_log->buf_.clear();
}
}
}
}
void __cyg_profile_func_enter(void* this_fn, void* callee_unused) {
if (tls_current_log == NULL) {
tls_current_log = kMagicBeingConstructed;
CygTlsLog* newlog = new CygTlsLog;
CHECK(newlog);
CygTlsLog::AddNewLog(newlog);
tls_current_log = newlog;
}
if (tls_current_log != kMagicBeingConstructed) {
tls_current_log->LogEnter(this_fn);
}
}
void __cyg_profile_func_exit(void* this_fn, void* call_site) {
}
}