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: 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> 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> 235 inline IndentString<Size>::IndentString(const int length) : 236 mOffset(calculateOffset(length)) 237 {} 238 239 template <int Size> 240 inline const char * IndentString<Size>::string() const 241 { 242 extern const char sIndentStringBuffer[]; 243 return sIndentStringBuffer + mOffset; 244 } 245 246 247 248 249 inline Debug * Debug::instance() 250 { return &sInstance; } 251 252 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 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 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 322 inline void Debug::increaseOffsetForCurrentThread() 323 { 324 #ifdef TI_UTILS_FUNCTION_LOGGER_ENABLE 325 addOffsetForCurrentThread(1); 326 #endif 327 } 328 329 330 inline void Debug::decreaseOffsetForCurrentThread() 331 { 332 #ifdef TI_UTILS_FUNCTION_LOGGER_ENABLE 333 addOffsetForCurrentThread(-1); 334 #endif 335 } 336 337 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 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 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 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