| // 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(); |
| } |