Home | History | Annotate | Download | only in libtiutils
      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