blob: 175c1daf968b569d5db4a5ca41486bf13934aff1 [file] [log] [blame]
// Copyright 2016 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/opengl/logger.h"
#include "android/base/files/PathUtils.h"
#include "android/base/memory/LazyInstance.h"
#include "android/base/synchronization/Lock.h"
#include "android/crashreport/CrashReporter.h"
#include <algorithm>
#include <fstream>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <string>
#include <sys/time.h>
#include <vector>
using android::base::AutoLock;
using android::base::Lock;
using android::base::PathUtils;
using android::crashreport::CrashReporter;
// The purpose of the OpenGL logger is to log
// information about such things as EGL initialization
// and possibly miscellanous OpenGL commands,
// in order to get a better idea of what is going on
// in crash reports.
// The OpenGLLogger implementation's initialization method
// by default uses the crash reporter's data directory.
static const int kBufferLen = 2048;
typedef std::pair<uint64_t, std::string> TimestampedLogEntry;
class OpenGLLogger {
public:
OpenGLLogger();
OpenGLLogger(const char* filename);
// Coarse log: Call this infrequently.
void writeCoarse(const char* str);
// Fine log: When we want to log very frequent events.
// Fine logs can be toggled on/off.
void writeFine(uint64_t time, const char* str);
void writeFineTimestamped(const char* str);
void startFineLog();
void stopFineLog();
static OpenGLLogger* get();
private:
Lock mLock;
bool mFineLogActive;
std::string mFileName;
std::ofstream mFileHandle;
std::string mFineLogFileName;
std::ofstream mFineLogFileHandle;
std::vector<TimestampedLogEntry> mFineLog;
DISALLOW_COPY_ASSIGN_AND_MOVE(OpenGLLogger);
};
::android::base::LazyInstance<OpenGLLogger> sOpenGLLogger = LAZY_INSTANCE_INIT;
OpenGLLogger* OpenGLLogger::get() {
return sOpenGLLogger.ptr();
}
OpenGLLogger::OpenGLLogger() :
mFineLogActive(false) {
const std::string& data_dir =
CrashReporter::get()->getDataExchangeDir();
mFileName = PathUtils::join(data_dir,
"opengl_log.txt");
mFileHandle.open(mFileName, std::ios::app);
mFineLogFileName = PathUtils::join(data_dir,
"opengl_cxt_log.txt");
mFineLogFileHandle.open(mFineLogFileName, std::ios::app);
}
OpenGLLogger::OpenGLLogger(const char* filename) :
mFileName(filename) {
mFileHandle.open(mFileName, std::ios::app);
}
void OpenGLLogger::writeCoarse(const char* str) {
if (mFileHandle) {
mFileHandle << str;
mFileHandle << std::endl;
}
}
void OpenGLLogger::writeFine(uint64_t time, const char* str) {
if (mFineLogActive) {
AutoLock lock(mLock);
mFineLog.emplace_back(time, str);
}
}
void OpenGLLogger::writeFineTimestamped(const char* str) {
if (mFineLogActive) {
char buf[kBufferLen] = {};
struct timeval tv;
gettimeofday(&tv, NULL);
uint64_t curr_micros = (tv.tv_usec) % 1000;
uint64_t curr_millis = (tv.tv_usec / 1000) % 1000;
uint64_t curr_secs = tv.tv_sec;
snprintf(buf, sizeof(buf) - 1,
"time_us="
"%" PRIu64 " s "
"%" PRIu64 " ms "
"%" PRIu64 " us | %s",
curr_secs,
curr_millis,
curr_micros,
str);
writeFine(curr_micros + 1000ULL * curr_millis +
1000ULL * 1000ULL * curr_secs, buf);
}
}
void OpenGLLogger::startFineLog() {
mFineLogActive = true;
}
void OpenGLLogger::stopFineLog() {
if (mFineLogActive) {
mFineLogActive = false;
// Only print message when fine-grained
// logging is turned on.
if (!mFineLog.empty()) {
fprintf(stderr,
"Writing fine-grained GL log to %s...",
mFineLogFileName.c_str());
}
// Sort log entries according to their timestamps.
// This is because the log entries might arrive
// out of order.
std::sort(mFineLog.begin(), mFineLog.end(),
[](const TimestampedLogEntry& x,
const TimestampedLogEntry& y) {
return x.first < y.first;
});
for (const auto& entry : mFineLog) {
// The fine log does not print newlines
// as it is used with the opengl debug
// printout in emugl, which adds
// newlines of its own.
mFineLogFileHandle << entry.second;
}
mFineLogFileHandle.close();
if (!mFineLog.empty()) {
fprintf(stderr, "done\n");
}
mFineLog.clear();
}
}
// C interface
void android_init_opengl_logger() {
OpenGLLogger* gl_log = OpenGLLogger::get();
gl_log->startFineLog();
}
void android_opengl_logger_write(const char* fmt, ...) {
char buf[kBufferLen] = {};
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
va_end(ap);
OpenGLLogger::get()->writeCoarse(buf);
}
void android_opengl_cxt_logger_write(const char* fmt, ...) {
char buf[kBufferLen] = {};
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
va_end(ap);
OpenGLLogger::get()->writeFineTimestamped(buf);
}
void android_stop_opengl_logger() {
OpenGLLogger* gl_log = OpenGLLogger::get();
gl_log->stopFineLog();
}