blob: 796a9377581e77b2d0c84b4b4094491651480406 [file] [log] [blame]
// Copyright 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "host-common/opengl/logger.h"
#include "aemu/base/files/PathUtils.h"
#include "aemu/base/synchronization/Lock.h"
#include <algorithm>
#include <fstream>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <string>
#include "aemu/base/msvc.h"
#ifndef _MSC_VER
#include <sys/time.h>
#endif
#include <vector>
using android::base::AutoLock;
using android::base::Lock;
using android::base::PathUtils;
// 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);
void stop();
// 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 writeFineTimestamped(const char* str);
void setLoggerFlags(AndroidOpenglLoggerFlags flags);
bool isFineLogging() const;
static OpenGLLogger* get();
private:
void writeFineLocked(uint64_t time, const char* str);
void stopFineLogLocked();
Lock mLock;
AndroidOpenglLoggerFlags mLoggerFlags = OPENGL_LOGGER_NONE;
uint64_t mPrevTimeUs = 0;
std::string mFileName;
std::ofstream mFileHandle;
std::string mFineLogFileName;
std::ofstream mFineLogFileHandle;
std::vector<TimestampedLogEntry> mFineLog;
DISALLOW_COPY_ASSIGN_AND_MOVE(OpenGLLogger);
};
static OpenGLLogger* sOpenGLLogger() {
static OpenGLLogger* g = new OpenGLLogger;
return g;
}
OpenGLLogger* OpenGLLogger::get() {
return sOpenGLLogger();
}
OpenGLLogger::OpenGLLogger() {
// #ifdef AEMU_MIN
return;
// #else
// 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);
// #endif
}
OpenGLLogger::OpenGLLogger(const char* filename) :
mFileName(filename) {
mFileHandle.open(mFileName, std::ios::app);
}
void OpenGLLogger::writeCoarse(const char* str) {
AutoLock lock(mLock);
if (mLoggerFlags & OPENGL_LOGGER_PRINT_TO_STDOUT) {
printf("%s\n", str);
}
if (mFileHandle) {
mFileHandle << str << std::endl;
}
}
void OpenGLLogger::stop() {
AutoLock lock(mLock);
stopFineLogLocked();
mFileHandle.close();
}
void OpenGLLogger::writeFineLocked(uint64_t time, const char* str) {
if (mLoggerFlags & OPENGL_LOGGER_PRINT_TO_STDOUT) {
printf("%s", str);
} else {
mFineLog.emplace_back(time, str);
}
}
void OpenGLLogger::writeFineTimestamped(const char* str) {
if (mLoggerFlags & OPENGL_LOGGER_DO_FINE_LOGGING) {
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;
uint64_t curr_us = tv.tv_sec * 1000000ULL + tv.tv_usec;
snprintf(buf, sizeof(buf) - 1,
"time_us="
"%" PRIu64 " s "
"%" PRIu64 " ms "
"%" PRIu64 " us deltaUs "
"%" PRIu64 " | %s",
curr_secs,
curr_millis,
curr_micros,
curr_us - mPrevTimeUs,
str);
AutoLock lock(mLock);
writeFineLocked(curr_micros + 1000ULL * curr_millis +
1000ULL * 1000ULL * curr_secs, buf);
mPrevTimeUs = curr_us;
}
}
void OpenGLLogger::setLoggerFlags(AndroidOpenglLoggerFlags flags) {
AutoLock lock(mLock);
bool needStopFineLog =
(mLoggerFlags & OPENGL_LOGGER_DO_FINE_LOGGING) &&
(!(flags & OPENGL_LOGGER_DO_FINE_LOGGING));
if (needStopFineLog) {
stopFineLogLocked();
}
mLoggerFlags = flags;
}
bool OpenGLLogger::isFineLogging() const {
// For speed, we'll just let this read of mLoggerFlags race.
return (mLoggerFlags & OPENGL_LOGGER_DO_FINE_LOGGING);
}
void OpenGLLogger::stopFineLogLocked() {
// 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;
});
if (mFineLogFileHandle) {
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::get();
}
void android_opengl_logger_set_flags(AndroidOpenglLoggerFlags flags) {
OpenGLLogger::get()->setLoggerFlags(flags);
}
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, ...) {
auto gl_log = OpenGLLogger::get();
if (!gl_log->isFineLogging()) return;
char buf[kBufferLen] = {};
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
va_end(ap);
gl_log->writeFineTimestamped(buf);
}
void android_stop_opengl_logger() {
OpenGLLogger* gl_log = OpenGLLogger::get();
gl_log->stop();
}