1 /*==============================================================================
2 Copyright(c) 2017 Intel Corporation
3
4 Permission is hereby granted, free of charge, to any person obtaining a
5 copy of this software and associated documentation files(the "Software"),
6 to deal in the Software without restriction, including without limitation
7 the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 and / or sell copies of the Software, and to permit persons to whom the
9 Software is furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included
12 in all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 OTHER DEALINGS IN THE SOFTWARE.
21 ============================================================================*/
22
23 #include "GmmLog.h"
24
25 #if GMM_LOG_AVAILABLE
26 #include "Internal/Common/GmmLibInc.h"
27 #include "Internal/Common/GmmLogger.h"
28
29 #if _WIN32
30 #include <process.h>
31 #include <memory>
32 #else
33 #include <stdio.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <fstream>
37 #include <linux/limits.h>
38 #endif
39
40 /// Logger instance shared by all of GmmLib within a process
41 #if(_DEBUG || _RELEASE_INTERNAL)
42 GmmLib::Logger &GmmLoggerPerProc = GmmLib::Logger::CreateGmmLogSingleton();
43 #endif
44
45 #if _WIN32
46 namespace spdlog
47 {
48 namespace sinks
49 {
50 /////////////////////////////////////////////////////////////////////////////////////
51 /// class defines a sink which prints the messages to the debugger
52 /////////////////////////////////////////////////////////////////////////////////////
53 class Debugger : public sink
54 {
log(const details::log_msg & msg)55 void log(const details::log_msg &msg) override
56 {
57 OutputDebugString(GMM_PREFIX_STR);
58 OutputDebugString(msg.formatted.str().c_str());
59 }
60
flush()61 void flush()
62 {
63 }
64 };
65 }
66 }
67 #endif
68
69 /////////////////////////////////////////////////////////////////////////////////////
70 /// Initializes Gmm Logger
71 ///
72 /// @return true if initialized successfully. false otherwise
73 /////////////////////////////////////////////////////////////////////////////////////
GmmLogInit()74 bool GmmLib::Logger::GmmLogInit()
75 {
76 std::string LogFilePath;
77 std::string ProcPath;
78 std::string ProcName;
79 int Pid = 0;
80
81 // Get logging method
82 #if _WIN32
83 uint32_t regkeyVal = 0;
84 if(Utility::GmmUMDReadRegistryFullPath(GMM_LOG_REG_KEY_SUB_PATH, GMM_LOG_TO_FILE, ®keyVal))
85 {
86 LogMethod = regkeyVal ? ToFile : ToOSLog;
87 }
88
89 if(Utility::GmmUMDReadRegistryFullPath(GMM_LOG_REG_KEY_SUB_PATH, GMM_LOG_LEVEL_REGKEY, ®keyVal))
90 {
91 switch(static_cast<GmmLogLevel>(regkeyVal))
92 {
93 case Trace:
94 LogLevel = spdlog::level::trace;
95 break;
96 case Info:
97 LogLevel = spdlog::level::info;
98 break;
99 case Error:
100 LogLevel = spdlog::level::err;
101 break;
102 case Critical:
103 LogLevel = spdlog::level::critical;
104 break;
105 case Off:
106 default:
107 LogLevel = spdlog::level::off;
108 break;
109 }
110 }
111
112 #endif
113 if(LogLevel == spdlog::level::off)
114 {
115 return false;
116 }
117
118 try
119 {
120 if(LogMethod == ToFile)
121 {
122 // Get process name
123 #if _WIN32
124 TCHAR ProcPathTChar[MAX_PATH];
125 GetModuleFileName(NULL, ProcPathTChar, MAX_PATH);
126 ProcPath = std::string(ProcPathTChar);
127
128 size_t PosOfLastSlash = ProcPath.find_last_of("\\") + 1;
129 size_t PosOfLastDot = ProcPath.find_last_of(".");
130
131 if(PosOfLastDot <= PosOfLastSlash || PosOfLastDot >= ProcPath.length() || PosOfLastSlash >= ProcPath.length())
132 {
133 ProcName = GMM_UNKNOWN_PROCESS;
134 }
135 else
136 {
137 ProcName = ProcPath.substr(PosOfLastSlash, PosOfLastDot - PosOfLastSlash);
138 }
139 #else
140 ProcPath = "Unknown_Proc_Path";
141 ProcName = GMM_UNKNOWN_PROCESS;
142
143 std::ifstream file;
144 file.open("/proc/self/cmdline");
145 if(file.is_open())
146 {
147 // Get process name
148 getline(file, ProcPath);
149
150 size_t PosOfLastSlash = ProcPath.find_last_of("/") + 1;
151 if(PosOfLastSlash >= ProcPath.length())
152 {
153 ProcName = GMM_UNKNOWN_PROCESS;
154 }
155 else
156 {
157 // "length-1" to remove null character
158 ProcName = ProcPath.substr(PosOfLastSlash, ProcPath.length() - 1);
159 }
160
161 file.close();
162 }
163
164 #endif
165
166 // Get process ID
167 #if _WIN32
168 Pid = _getpid();
169 #else
170 Pid = getpid();
171 #endif
172 std::string PidStr = std::to_string(Pid);
173
174 // TODO: Multiple GmmLib instance can be running in the same process. In that case, the file name will be
175 // the same for two instances. Figure out a way to differentiate between the two instances.
176 #if _WIN32
177 LogFilePath = std::string("c:\\") + std::string(GMM_LOG_FILENAME) + "_" + ProcName + "_" + PidStr;
178 #else
179 LogFilePath = std::string(".//") + std::string(GMM_LOG_FILENAME) + "" + ProcName + "_" + PidStr;
180 #endif
181 // Create logger
182 SpdLogger = spdlog::rotating_logger_mt(GMM_LOGGER_NAME,
183 LogFilePath,
184 GMM_LOG_FILE_SIZE,
185 GMM_ROTATE_FILE_NUMBER);
186
187 // Log process path
188 SpdLogger->set_pattern("Process path: %v");
189 SpdLogger->info(ProcPath.c_str());
190 }
191 else
192 {
193 #if defined(_WIN32)
194 // Log to debugger
195 auto debugger_sink = std::make_shared<spdlog::sinks::Debugger>();
196 SpdLogger = std::make_shared<spdlog::logger>(GMM_LOGGER_NAME, debugger_sink);
197 #elif defined(__ANDROID__)
198 // Log to logcat
199 SpdLogger = spdlog::android_logger(GMM_LOGGER_NAME, GMM_LOG_TAG);
200 #elif defined(__linux__)
201 // Log to syslog
202 SpdLogger = spdlog::syslog_logger(GMM_LOGGER_NAME, GMM_LOG_TAG, 1 /*Log Pid*/);
203 #else
204 __GMM_ASSERT(0);
205 return false;
206 #endif
207 }
208 }
209 catch(const spdlog::spdlog_ex &ex)
210 {
211 __GMM_ASSERT(0);
212 return false;
213 }
214
215 // Set log level
216 SpdLogger->set_level(LogLevel);
217 // Set log pattern
218 SpdLogger->set_pattern("[%T.%e] [Thread %t] [%l] %v"); // [Time] [Thread id] [Log Level] [Text to Log]
219
220 return true;
221 }
222
223 /////////////////////////////////////////////////////////////////////////////////////
224 /// Gmm Logger constructor
225 /////////////////////////////////////////////////////////////////////////////////////
Logger()226 GmmLib::Logger::Logger()
227 : LogMethod(ToOSLog),
228 LogLevel(spdlog::level::off)
229 {
230 if(!GmmLogInit())
231 {
232 spdlog::set_level(spdlog::level::off);
233 }
234 }
235
236 /////////////////////////////////////////////////////////////////////////////////////
237 /// Gmm Logger Destructor
238 /////////////////////////////////////////////////////////////////////////////////////
~Logger()239 GmmLib::Logger::~Logger()
240 {
241 if(SpdLogger)
242 {
243 SpdLogger->flush();
244 }
245 }
246
247 /////////////////////////////////////////////////////////////////////////////////////
248 /// Gmm Logger C wrappers
249 /////////////////////////////////////////////////////////////////////////////////////
250 #if !_WIN32
251 // Linux/Android replacement for MS version of _vscprintf
vscprintf_lin(const char * msg,va_list args)252 inline int vscprintf_lin(const char *msg, va_list args)
253 {
254 char c;
255 va_list args_cpy;
256
257 // Copy `args' to prevent internal pointer modification from vsnprintf
258 va_copy(args_cpy, args);
259 int len = vsnprintf(&c, 1, msg, args_cpy);
260 va_end(args_cpy);
261 return len;
262 }
263 #endif
264
265 /////////////////////////////////////////////////////////////////////////////////////
266 /// Gmm Logger C wrapper for GMM_LOG_* Macros
267 /////////////////////////////////////////////////////////////////////////////////////
GmmLibLogging(GmmLogLevel Level,const char * str,...)268 extern "C" void GMM_STDCALL GmmLibLogging(GmmLogLevel Level, const char *str, ...)
269 {
270 va_list args;
271
272 if(GmmLoggerPerProc.SpdLogger)
273 {
274 va_start(args, str);
275
276 #if _WIN32
277 const size_t length = _vscprintf(str, args);
278 #else
279 const size_t length = vscprintf_lin(str, args);
280 #endif
281
282 char *temp = new char[length + 1];
283
284 if(temp)
285 {
286
287 #if _WIN32
288 vsprintf_s(temp, length + 1, str, args);
289 #else
290 vsnprintf(temp, length + 1, str, args);
291 #endif
292
293 switch(Level)
294 {
295 case Trace:
296 // Set log level to trace if we want trace msges to be printed
297 //GmmLoggerPerProc.SpdLogger->set_level(spdlog::level::trace);
298 GmmLoggerPerProc.SpdLogger->trace(temp);
299 break;
300 case Info:
301 // Set log level to info if we want info msges to be printed
302 //GmmLoggerPerProc.SpdLogger->set_level(spdlog::level::info);
303 GmmLoggerPerProc.SpdLogger->info(temp);
304 break;
305 case Error:
306 GmmLoggerPerProc.SpdLogger->critical(temp);
307 break;
308 default:
309 break;
310 }
311
312 delete[] temp;
313 }
314 }
315
316 va_end(args);
317 }
318
319 #endif //#if GMM_LOG_AVAILABLE
320