• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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