1 // Copyright 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "host-common/opengl/logger.h"
16 
17 #include <inttypes.h>
18 #include <stdarg.h>
19 #include <stdio.h>
20 
21 #include <algorithm>
22 #include <fstream>
23 #include <string>
24 
25 #include "aemu/base/files/PathUtils.h"
26 #include "aemu/base/msvc.h"
27 #include "aemu/base/synchronization/Lock.h"
28 
29 #ifndef _MSC_VER
30 #include <sys/time.h>
31 #endif
32 #include <vector>
33 
34 using android::base::AutoLock;
35 using android::base::Lock;
36 using android::base::PathUtils;
37 
38 // The purpose of the OpenGL logger is to log
39 // information about such things as EGL initialization
40 // and possibly miscellanous OpenGL commands,
41 // in order to get a better idea of what is going on
42 // in crash reports.
43 
44 // The OpenGLLogger implementation's initialization method
45 // by default uses the crash reporter's data directory.
46 
47 static const int kBufferLen = 2048;
48 
49 typedef std::pair<uint64_t, std::string> TimestampedLogEntry;
50 
51 class OpenGLLogger {
52    public:
53     OpenGLLogger() = default;
54     OpenGLLogger(const char* filename);
55     void stop();
56 
57     // Coarse log: Call this infrequently.
58     void writeCoarse(const char* str);
59 
60     // Fine log: When we want to log very frequent events.
61     // Fine logs can be toggled on/off.
62     void writeFineTimestamped(const char* str);
63 
64     void setLoggerFlags(AndroidOpenglLoggerFlags flags);
65     bool isFineLogging() const;
66 
67     static OpenGLLogger* get();
68 
69    private:
70     void writeFineLocked(uint64_t time, const char* str);
71     void stopFineLogLocked();
72 
73     Lock mLock;
74     AndroidOpenglLoggerFlags mLoggerFlags = OPENGL_LOGGER_NONE;
75     uint64_t mPrevTimeUs = 0;
76     std::string mFileName;
77     std::ofstream mFileHandle;
78     std::string mFineLogFileName;
79     std::ofstream mFineLogFileHandle;
80     std::vector<TimestampedLogEntry> mFineLog;
81     DISALLOW_COPY_ASSIGN_AND_MOVE(OpenGLLogger);
82 };
83 
sOpenGLLogger()84 static OpenGLLogger* sOpenGLLogger() {
85     static OpenGLLogger sLogger;
86     return &sLogger;
87 }
88 
get()89 OpenGLLogger* OpenGLLogger::get() { return sOpenGLLogger(); }
90 
91 
OpenGLLogger(const char * filename)92 OpenGLLogger::OpenGLLogger(const char* filename) : mFileName(filename) {
93     mFileHandle.open(mFileName, std::ios::app);
94 }
95 
writeCoarse(const char * str)96 void OpenGLLogger::writeCoarse(const char* str) {
97     AutoLock lock(mLock);
98     if (mLoggerFlags & OPENGL_LOGGER_PRINT_TO_STDOUT) {
99         printf("%s\n", str);
100     }
101     if (mFileHandle) {
102         mFileHandle << str << std::endl;
103     }
104 }
105 
stop()106 void OpenGLLogger::stop() {
107     AutoLock lock(mLock);
108     stopFineLogLocked();
109     mFileHandle.close();
110 }
111 
writeFineLocked(uint64_t time,const char * str)112 void OpenGLLogger::writeFineLocked(uint64_t time, const char* str) {
113     if (mLoggerFlags & OPENGL_LOGGER_PRINT_TO_STDOUT) {
114         printf("%s", str);
115     } else {
116         mFineLog.emplace_back(time, str);
117     }
118 }
119 
writeFineTimestamped(const char * str)120 void OpenGLLogger::writeFineTimestamped(const char* str) {
121     if (mLoggerFlags & OPENGL_LOGGER_DO_FINE_LOGGING) {
122         char buf[kBufferLen] = {};
123         struct timeval tv;
124         gettimeofday(&tv, NULL);
125 
126         uint64_t curr_micros = (tv.tv_usec) % 1000;
127         uint64_t curr_millis = (tv.tv_usec / 1000) % 1000;
128         uint64_t curr_secs = tv.tv_sec;
129         uint64_t curr_us = tv.tv_sec * 1000000ULL + tv.tv_usec;
130         snprintf(buf, sizeof(buf) - 1,
131                  "time_us="
132                  "%" PRIu64
133                  " s "
134                  "%" PRIu64
135                  " ms "
136                  "%" PRIu64
137                  " us deltaUs "
138                  "%" PRIu64 " | %s",
139                  curr_secs, curr_millis, curr_micros, curr_us - mPrevTimeUs, str);
140         AutoLock lock(mLock);
141         writeFineLocked(curr_micros + 1000ULL * curr_millis + 1000ULL * 1000ULL * curr_secs, buf);
142         mPrevTimeUs = curr_us;
143     }
144 }
145 
setLoggerFlags(AndroidOpenglLoggerFlags flags)146 void OpenGLLogger::setLoggerFlags(AndroidOpenglLoggerFlags flags) {
147     AutoLock lock(mLock);
148     bool needStopFineLog = (mLoggerFlags & OPENGL_LOGGER_DO_FINE_LOGGING) &&
149                            (!(flags & OPENGL_LOGGER_DO_FINE_LOGGING));
150 
151     if (needStopFineLog) {
152         stopFineLogLocked();
153     }
154 
155     mLoggerFlags = flags;
156 }
157 
isFineLogging() const158 bool OpenGLLogger::isFineLogging() const {
159     // For speed, we'll just let this read of mLoggerFlags race.
160     return (mLoggerFlags & OPENGL_LOGGER_DO_FINE_LOGGING);
161 }
162 
stopFineLogLocked()163 void OpenGLLogger::stopFineLogLocked() {
164     // Only print message when fine-grained
165     // logging is turned on.
166     if (!mFineLog.empty()) {
167         fprintf(stderr, "Writing fine-grained GL log to %s...", mFineLogFileName.c_str());
168     }
169 
170     // Sort log entries according to their timestamps.
171     // This is because the log entries might arrive
172     // out of order.
173     std::sort(mFineLog.begin(), mFineLog.end(),
174               [](const TimestampedLogEntry& x, const TimestampedLogEntry& y) {
175                   return x.first < y.first;
176               });
177 
178     if (mFineLogFileHandle) {
179         for (const auto& entry : mFineLog) {
180             // The fine log does not print newlines
181             // as it is used with the opengl debug
182             // printout in emugl, which adds
183             // newlines of its own.
184             mFineLogFileHandle << entry.second;
185         }
186         mFineLogFileHandle.close();
187         if (!mFineLog.empty()) {
188             fprintf(stderr, "done\n");
189         }
190     }
191     mFineLog.clear();
192 }
193 
194 // C interface
195 
android_init_opengl_logger()196 void android_init_opengl_logger() { OpenGLLogger::get(); }
197 
android_opengl_logger_set_flags(AndroidOpenglLoggerFlags flags)198 void android_opengl_logger_set_flags(AndroidOpenglLoggerFlags flags) {
199     OpenGLLogger::get()->setLoggerFlags(flags);
200 }
201 
android_opengl_logger_write(char severity,const char * file,unsigned int line,int64_t timestamp_us,const char * message)202 void android_opengl_logger_write(char severity, const char* file, unsigned int line,
203                                  int64_t timestamp_us, const char* message) {
204     char buf[kBufferLen] = {};
205     auto gl_log = OpenGLLogger::get();
206 
207     if (severity == 'V' || severity == 'D') {
208         // Not logging details if it is not requested
209         if (!gl_log->isFineLogging()) {
210             return;
211         }
212 
213         snprintf(buf, sizeof(buf) - 1, "%c %s:%d %s", severity, file, line, message);
214         gl_log->writeFineTimestamped(buf);
215         return;
216     }
217 
218     // Other log levels..
219     snprintf(buf, sizeof(buf) - 1, "%c %s:%d %s", severity, file, line, message);
220     gl_log->writeCoarse(buf);
221 }
222 
android_stop_opengl_logger()223 void android_stop_opengl_logger() {
224     OpenGLLogger* gl_log = OpenGLLogger::get();
225     gl_log->stop();
226 }
227