1 /*
2 * Copyright (C) Texas Instruments - http://www.ti.com/
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef DEBUG_UTILS_H
18 #define DEBUG_UTILS_H
19
20 #include <android/log.h>
21 #include <utils/threads.h>
22 #include <utils/Vector.h>
23
24
25
26
27 namespace Ti {
28
29
30
31
32 // use 2 space characters for call stack indent
33 static const int kFunctionLoggerIndentSize = 2;
34
35
36
37
38 template <int Size = kFunctionLoggerIndentSize>
39 class IndentString
40 {
41 public:
42 IndentString(int length);
43
44 const char * string() const;
45
46 private:
47 int calculateOffset(int length) const;
48
49 private:
50 const int mOffset;
51 };
52
53
54
55
56 class Debug
57 {
58 public:
59 static Debug * instance();
60
61 int offsetForCurrentThread();
62 void log(int priority, const char * format, ...);
63
64 private:
65 class ThreadInfo
66 {
67 public:
ThreadInfo()68 ThreadInfo() :
69 threadId(0), callOffset(0)
70 {}
71
72 volatile int32_t threadId;
73 int callOffset;
74 };
75
76 class Data : public android::RefBase
77 {
78 public:
79 android::Vector<ThreadInfo*> threads;
80 };
81
82 private:
83 // called from FunctionLogger
84 void increaseOffsetForCurrentThread();
85 void decreaseOffsetForCurrentThread();
86
87 private:
88 Debug();
89
90 void grow();
91 ThreadInfo * registerThread(Data * data, int32_t threadId);
92 ThreadInfo * findCurrentThreadInfo();
93 void addOffsetForCurrentThread(int offset);
94
95 private:
96 static Debug sInstance;
97
98 mutable android::Mutex mMutex;
99 android::sp<Data> mData;
100
101 friend class FunctionLogger;
102 };
103
104
105
106
107 class FunctionLogger
108 {
109 public:
110 FunctionLogger(const char * file, int line, const char * function);
111 ~FunctionLogger();
112
113 void setExitLine(int line);
114
115 private:
116 const char * const mFile;
117 const int mLine;
118 const char * const mFunction;
119 const void * const mThreadId;
120 int mExitLine;
121 };
122
123
124
125
126 #ifdef TI_UTILS_FUNCTION_LOGGER_ENABLE
127 # define LOG_FUNCTION_NAME Ti::FunctionLogger __function_logger_instance(__FILE__, __LINE__, __FUNCTION__);
128 # define LOG_FUNCTION_NAME_EXIT __function_logger_instance.setExitLine(__LINE__);
129 #else
130 # define LOG_FUNCTION_NAME int __function_logger_instance;
131 # define LOG_FUNCTION_NAME_EXIT (void*)__function_logger_instance;
132 #endif
133
134 #ifdef TI_UTILS_DEBUG_USE_TIMESTAMPS
135 // truncate timestamp to 1000 seconds to fit into 6 characters
136 # define TI_UTILS_DEBUG_TIMESTAMP_TOKEN "[%06d] "
137 # define TI_UTILS_DEBUG_TIMESTAMP_VARIABLE static_cast<int>(nanoseconds_to_milliseconds(systemTime()) % 1000000),
138 #else
139 # define TI_UTILS_DEBUG_TIMESTAMP_TOKEN
140 # define TI_UTILS_DEBUG_TIMESTAMP_VARIABLE
141 #endif
142
143
144
145
146 #define DBGUTILS_LOGV_FULL(priority, file, line, function, format, ...) \
147 do \
148 { \
149 Ti::Debug * const debug = Ti::Debug::instance(); \
150 debug->log(priority, format, \
151 TI_UTILS_DEBUG_TIMESTAMP_VARIABLE \
152 reinterpret_cast<int>(androidGetThreadId()), \
153 Ti::IndentString<>(debug->offsetForCurrentThread()).string(), \
154 file, line, function, __VA_ARGS__); \
155 } while (0)
156
157 #define DBGUTILS_LOGV(...) DBGUTILS_LOGV_FULL(ANDROID_LOG_VERBOSE, __FILE__, __LINE__, __FUNCTION__, TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - " __VA_ARGS__, "")
158 #define DBGUTILS_LOGD(...) DBGUTILS_LOGV_FULL(ANDROID_LOG_DEBUG, __FILE__, __LINE__, __FUNCTION__, TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - " __VA_ARGS__, "")
159 #define DBGUTILS_LOGI(...) DBGUTILS_LOGV_FULL(ANDROID_LOG_INFO, __FILE__, __LINE__, __FUNCTION__, TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - " __VA_ARGS__, "")
160 #define DBGUTILS_LOGW(...) DBGUTILS_LOGV_FULL(ANDROID_LOG_WARN, __FILE__, __LINE__, __FUNCTION__, TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - " __VA_ARGS__, "")
161 #define DBGUTILS_LOGE(...) DBGUTILS_LOGV_FULL(ANDROID_LOG_ERROR, __FILE__, __LINE__, __FUNCTION__, TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - " __VA_ARGS__, "")
162 #define DBGUTILS_LOGF(...) DBGUTILS_LOGV_FULL(ANDROID_LOG_FATAL, __FILE__, __LINE__, __FUNCTION__, TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - " __VA_ARGS__, "")
163
164 #define DBGUTILS_LOGVA DBGUTILS_LOGV
165 #define DBGUTILS_LOGVB DBGUTILS_LOGV
166
167 #define DBGUTILS_LOGDA DBGUTILS_LOGD
168 #define DBGUTILS_LOGDB DBGUTILS_LOGD
169
170 #define DBGUTILS_LOGEA DBGUTILS_LOGE
171 #define DBGUTILS_LOGEB DBGUTILS_LOGE
172
173 // asserts
174 #define _DBGUTILS_PLAIN_ASSERT(condition) \
175 do \
176 { \
177 if ( !(condition) ) \
178 { \
179 __android_log_print(ANDROID_LOG_FATAL, "Ti::Debug", \
180 "Condition failed: " #condition); \
181 __android_log_print(ANDROID_LOG_FATAL, "Ti::Debug", \
182 "Aborting process..."); \
183 abort(); \
184 } \
185 } while (0)
186
187 #define _DBGUTILS_PLAIN_ASSERT_X(condition, ...) \
188 do \
189 { \
190 if ( !(condition) ) \
191 { \
192 __android_log_print(ANDROID_LOG_FATAL, "Ti::Debug", \
193 "Condition failed: " #condition ": " __VA_ARGS__); \
194 __android_log_print(ANDROID_LOG_FATAL, "Ti::Debug", \
195 "Aborting process..."); \
196 abort(); \
197 } \
198 } while (0)
199
200 #define DBGUTILS_ASSERT(condition) \
201 do \
202 { \
203 if ( !(condition) ) \
204 { \
205 DBGUTILS_LOGF("Condition failed: " #condition); \
206 DBGUTILS_LOGF("Aborting process..."); \
207 abort(); \
208 } \
209 } while (0)
210
211 #define DBGUTILS_ASSERT_X(condition, ...) \
212 do \
213 { \
214 if ( !(condition) ) \
215 { \
216 DBGUTILS_LOGF("Condition failed: " #condition ": " __VA_ARGS__); \
217 DBGUTILS_LOGF("Aborting process..."); \
218 abort(); \
219 } \
220 } while (0)
221
222
223
224
225 static const int kIndentStringMaxLength = 128;
226
227 template <int Size>
calculateOffset(const int length)228 inline int IndentString<Size>::calculateOffset(const int length) const
229 {
230 const int offset = kIndentStringMaxLength - length*Size;
231 return offset < 0 ? 0 : offset;
232 }
233
234 template <int Size>
IndentString(const int length)235 inline IndentString<Size>::IndentString(const int length) :
236 mOffset(calculateOffset(length))
237 {}
238
239 template <int Size>
string()240 inline const char * IndentString<Size>::string() const
241 {
242 extern const char sIndentStringBuffer[];
243 return sIndentStringBuffer + mOffset;
244 }
245
246
247
248
instance()249 inline Debug * Debug::instance()
250 { return &sInstance; }
251
252
findCurrentThreadInfo()253 inline Debug::ThreadInfo * Debug::findCurrentThreadInfo()
254 {
255 // retain reference to threads data
256 android::sp<Data> data = mData;
257
258 // iterate over threads to locate thread id,
259 // this is safe from race conditions because each thread
260 // is able to modify only his own ThreadInfo structure
261 const int32_t threadId = reinterpret_cast<int32_t>(androidGetThreadId());
262 const int size = int(data->threads.size());
263 for ( int i = 0; i < size; ++i )
264 {
265 ThreadInfo * const threadInfo = data->threads.itemAt(i);
266 if ( threadInfo->threadId == threadId )
267 return threadInfo;
268 }
269
270 // this thread has not been registered yet,
271 // try to fing empty thread info slot
272 while ( true )
273 {
274 ThreadInfo * const threadInfo = registerThread(data.get(), threadId);
275 if ( threadInfo )
276 return threadInfo;
277
278 // failed registering thread, because all slots are occupied
279 // grow the data and try again
280 grow();
281
282 data = mData;
283 }
284
285 // should never reach here
286 _DBGUTILS_PLAIN_ASSERT(false);
287 return 0;
288 }
289
290
addOffsetForCurrentThread(const int offset)291 inline void Debug::addOffsetForCurrentThread(const int offset)
292 {
293 if ( offset == 0 )
294 return;
295
296 ThreadInfo * const threadInfo = findCurrentThreadInfo();
297 _DBGUTILS_PLAIN_ASSERT(threadInfo);
298
299 threadInfo->callOffset += offset;
300
301 if ( threadInfo->callOffset == 0 )
302 {
303 // thread call stack has dropped to zero, unregister it
304 android_atomic_acquire_store(0, &threadInfo->threadId);
305 }
306 }
307
308
offsetForCurrentThread()309 inline int Debug::offsetForCurrentThread()
310 {
311 #ifdef TI_UTILS_FUNCTION_LOGGER_ENABLE
312 ThreadInfo * const threadInfo = findCurrentThreadInfo();
313 _DBGUTILS_PLAIN_ASSERT(threadInfo);
314
315 return threadInfo->callOffset;
316 #else
317 return 0;
318 #endif
319 }
320
321
increaseOffsetForCurrentThread()322 inline void Debug::increaseOffsetForCurrentThread()
323 {
324 #ifdef TI_UTILS_FUNCTION_LOGGER_ENABLE
325 addOffsetForCurrentThread(1);
326 #endif
327 }
328
329
decreaseOffsetForCurrentThread()330 inline void Debug::decreaseOffsetForCurrentThread()
331 {
332 #ifdef TI_UTILS_FUNCTION_LOGGER_ENABLE
333 addOffsetForCurrentThread(-1);
334 #endif
335 }
336
337
log(const int priority,const char * const format,...)338 inline void Debug::log(const int priority, const char * const format, ...)
339 {
340 va_list args;
341 va_start(args, format);
342 __android_log_vprint(priority, LOG_TAG, format, args);
343 va_end(args);
344 }
345
346
347
348
FunctionLogger(const char * const file,const int line,const char * const function)349 inline FunctionLogger::FunctionLogger(const char * const file, const int line, const char * const function) :
350 mFile(file), mLine(line), mFunction(function), mThreadId(androidGetThreadId()), mExitLine(-1)
351 {
352 Debug * const debug = Debug::instance();
353 debug->increaseOffsetForCurrentThread();
354 android_printLog(ANDROID_LOG_DEBUG, LOG_TAG,
355 TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s+ %s:%d %s - ENTER",
356 TI_UTILS_DEBUG_TIMESTAMP_VARIABLE
357 (int)mThreadId, IndentString<>(debug->offsetForCurrentThread()).string(),
358 mFile, mLine, mFunction);
359 }
360
361
~FunctionLogger()362 inline FunctionLogger::~FunctionLogger()
363 {
364 Debug * const debug = Debug::instance();
365 android_printLog(ANDROID_LOG_DEBUG, LOG_TAG,
366 TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s- %s:%d %s - EXIT",
367 TI_UTILS_DEBUG_TIMESTAMP_VARIABLE
368 (int)mThreadId, IndentString<>(debug->offsetForCurrentThread()).string(),
369 mFile, mExitLine == -1 ? mLine : mExitLine, mFunction);
370 debug->decreaseOffsetForCurrentThread();
371 }
372
373
setExitLine(const int line)374 inline void FunctionLogger::setExitLine(const int line)
375 {
376 if ( mExitLine != -1 )
377 {
378 Debug * const debug = Debug::instance();
379 android_printLog(ANDROID_LOG_DEBUG, LOG_TAG,
380 TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - Double function exit trace detected. Previous: %d",
381 TI_UTILS_DEBUG_TIMESTAMP_VARIABLE
382 (int)mThreadId, IndentString<>(debug->offsetForCurrentThread()).string(),
383 mFile, line, mFunction, mExitLine);
384 }
385
386 mExitLine = line;
387 }
388
389
390
391
392 } // namespace Ti
393
394
395
396
397 #endif //DEBUG_UTILS_H
398