| /* |
| * Copyright (C) Texas Instruments - http://www.ti.com/ |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef DEBUG_UTILS_H |
| #define DEBUG_UTILS_H |
| |
| #include <android/log.h> |
| #include <utils/threads.h> |
| #include <utils/Vector.h> |
| |
| |
| |
| |
| namespace Ti { |
| |
| |
| |
| |
| // use 2 space characters for call stack indent |
| static const int kFunctionLoggerIndentSize = 2; |
| |
| |
| |
| |
| template <int Size = kFunctionLoggerIndentSize> |
| class IndentString |
| { |
| public: |
| IndentString(int length); |
| |
| const char * string() const; |
| |
| private: |
| int calculateOffset(int length) const; |
| |
| private: |
| const int mOffset; |
| }; |
| |
| |
| |
| |
| class Debug |
| { |
| public: |
| static Debug * instance(); |
| |
| int offsetForCurrentThread(); |
| void log(int priority, const char * format, ...); |
| |
| private: |
| class ThreadInfo |
| { |
| public: |
| ThreadInfo() : |
| threadId(0), callOffset(0) |
| {} |
| |
| volatile int32_t threadId; |
| int callOffset; |
| }; |
| |
| class Data : public android::RefBase |
| { |
| public: |
| android::Vector<ThreadInfo*> threads; |
| }; |
| |
| private: |
| // called from FunctionLogger |
| void increaseOffsetForCurrentThread(); |
| void decreaseOffsetForCurrentThread(); |
| |
| private: |
| Debug(); |
| |
| void grow(); |
| ThreadInfo * registerThread(Data * data, int32_t threadId); |
| ThreadInfo * findCurrentThreadInfo(); |
| void addOffsetForCurrentThread(int offset); |
| |
| private: |
| static Debug sInstance; |
| |
| mutable android::Mutex mMutex; |
| android::sp<Data> mData; |
| |
| friend class FunctionLogger; |
| }; |
| |
| |
| |
| |
| class FunctionLogger |
| { |
| public: |
| FunctionLogger(const char * file, int line, const char * function); |
| ~FunctionLogger(); |
| |
| void setExitLine(int line); |
| |
| private: |
| const char * const mFile; |
| const int mLine; |
| const char * const mFunction; |
| const void * const mThreadId; |
| int mExitLine; |
| }; |
| |
| |
| |
| |
| #ifdef TI_UTILS_FUNCTION_LOGGER_ENABLE |
| # define LOG_FUNCTION_NAME Ti::FunctionLogger __function_logger_instance(__FILE__, __LINE__, __FUNCTION__); |
| # define LOG_FUNCTION_NAME_EXIT __function_logger_instance.setExitLine(__LINE__); |
| #else |
| # define LOG_FUNCTION_NAME int __function_logger_instance; |
| # define LOG_FUNCTION_NAME_EXIT (void*)__function_logger_instance; |
| #endif |
| |
| #ifdef TI_UTILS_DEBUG_USE_TIMESTAMPS |
| // truncate timestamp to 1000 seconds to fit into 6 characters |
| # define TI_UTILS_DEBUG_TIMESTAMP_TOKEN "[%06d] " |
| # define TI_UTILS_DEBUG_TIMESTAMP_VARIABLE static_cast<int>(nanoseconds_to_milliseconds(systemTime()) % 1000000), |
| #else |
| # define TI_UTILS_DEBUG_TIMESTAMP_TOKEN |
| # define TI_UTILS_DEBUG_TIMESTAMP_VARIABLE |
| #endif |
| |
| |
| |
| |
| #define DBGUTILS_LOGV_FULL(priority, file, line, function, format, ...) \ |
| do \ |
| { \ |
| Ti::Debug * const debug = Ti::Debug::instance(); \ |
| debug->log(priority, format, \ |
| TI_UTILS_DEBUG_TIMESTAMP_VARIABLE \ |
| reinterpret_cast<int>(androidGetThreadId()), \ |
| Ti::IndentString<>(debug->offsetForCurrentThread()).string(), \ |
| file, line, function, __VA_ARGS__); \ |
| } while (0) |
| |
| #define DBGUTILS_LOGV(...) DBGUTILS_LOGV_FULL(ANDROID_LOG_VERBOSE, __FILE__, __LINE__, __FUNCTION__, TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - " __VA_ARGS__, "") |
| #define DBGUTILS_LOGD(...) DBGUTILS_LOGV_FULL(ANDROID_LOG_DEBUG, __FILE__, __LINE__, __FUNCTION__, TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - " __VA_ARGS__, "") |
| #define DBGUTILS_LOGI(...) DBGUTILS_LOGV_FULL(ANDROID_LOG_INFO, __FILE__, __LINE__, __FUNCTION__, TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - " __VA_ARGS__, "") |
| #define DBGUTILS_LOGW(...) DBGUTILS_LOGV_FULL(ANDROID_LOG_WARN, __FILE__, __LINE__, __FUNCTION__, TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - " __VA_ARGS__, "") |
| #define DBGUTILS_LOGE(...) DBGUTILS_LOGV_FULL(ANDROID_LOG_ERROR, __FILE__, __LINE__, __FUNCTION__, TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - " __VA_ARGS__, "") |
| #define DBGUTILS_LOGF(...) DBGUTILS_LOGV_FULL(ANDROID_LOG_FATAL, __FILE__, __LINE__, __FUNCTION__, TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - " __VA_ARGS__, "") |
| |
| #define DBGUTILS_LOGVA DBGUTILS_LOGV |
| #define DBGUTILS_LOGVB DBGUTILS_LOGV |
| |
| #define DBGUTILS_LOGDA DBGUTILS_LOGD |
| #define DBGUTILS_LOGDB DBGUTILS_LOGD |
| |
| #define DBGUTILS_LOGEA DBGUTILS_LOGE |
| #define DBGUTILS_LOGEB DBGUTILS_LOGE |
| |
| // asserts |
| #define _DBGUTILS_PLAIN_ASSERT(condition) \ |
| do \ |
| { \ |
| if ( !(condition) ) \ |
| { \ |
| __android_log_print(ANDROID_LOG_FATAL, "Ti::Debug", \ |
| "Condition failed: " #condition); \ |
| __android_log_print(ANDROID_LOG_FATAL, "Ti::Debug", \ |
| "Aborting process..."); \ |
| abort(); \ |
| } \ |
| } while (0) |
| |
| #define _DBGUTILS_PLAIN_ASSERT_X(condition, ...) \ |
| do \ |
| { \ |
| if ( !(condition) ) \ |
| { \ |
| __android_log_print(ANDROID_LOG_FATAL, "Ti::Debug", \ |
| "Condition failed: " #condition ": " __VA_ARGS__); \ |
| __android_log_print(ANDROID_LOG_FATAL, "Ti::Debug", \ |
| "Aborting process..."); \ |
| abort(); \ |
| } \ |
| } while (0) |
| |
| #define DBGUTILS_ASSERT(condition) \ |
| do \ |
| { \ |
| if ( !(condition) ) \ |
| { \ |
| DBGUTILS_LOGF("Condition failed: " #condition); \ |
| DBGUTILS_LOGF("Aborting process..."); \ |
| abort(); \ |
| } \ |
| } while (0) |
| |
| #define DBGUTILS_ASSERT_X(condition, ...) \ |
| do \ |
| { \ |
| if ( !(condition) ) \ |
| { \ |
| DBGUTILS_LOGF("Condition failed: " #condition ": " __VA_ARGS__); \ |
| DBGUTILS_LOGF("Aborting process..."); \ |
| abort(); \ |
| } \ |
| } while (0) |
| |
| |
| |
| |
| static const int kIndentStringMaxLength = 128; |
| |
| template <int Size> |
| inline int IndentString<Size>::calculateOffset(const int length) const |
| { |
| const int offset = kIndentStringMaxLength - length*Size; |
| return offset < 0 ? 0 : offset; |
| } |
| |
| template <int Size> |
| inline IndentString<Size>::IndentString(const int length) : |
| mOffset(calculateOffset(length)) |
| {} |
| |
| template <int Size> |
| inline const char * IndentString<Size>::string() const |
| { |
| extern const char sIndentStringBuffer[]; |
| return sIndentStringBuffer + mOffset; |
| } |
| |
| |
| |
| |
| inline Debug * Debug::instance() |
| { return &sInstance; } |
| |
| |
| inline Debug::ThreadInfo * Debug::findCurrentThreadInfo() |
| { |
| // retain reference to threads data |
| android::sp<Data> data = mData; |
| |
| // iterate over threads to locate thread id, |
| // this is safe from race conditions because each thread |
| // is able to modify only his own ThreadInfo structure |
| const int32_t threadId = reinterpret_cast<int32_t>(androidGetThreadId()); |
| const int size = int(data->threads.size()); |
| for ( int i = 0; i < size; ++i ) |
| { |
| ThreadInfo * const threadInfo = data->threads.itemAt(i); |
| if ( threadInfo->threadId == threadId ) |
| return threadInfo; |
| } |
| |
| // this thread has not been registered yet, |
| // try to fing empty thread info slot |
| while ( true ) |
| { |
| ThreadInfo * const threadInfo = registerThread(data.get(), threadId); |
| if ( threadInfo ) |
| return threadInfo; |
| |
| // failed registering thread, because all slots are occupied |
| // grow the data and try again |
| grow(); |
| |
| data = mData; |
| } |
| |
| // should never reach here |
| _DBGUTILS_PLAIN_ASSERT(false); |
| return 0; |
| } |
| |
| |
| inline void Debug::addOffsetForCurrentThread(const int offset) |
| { |
| if ( offset == 0 ) |
| return; |
| |
| ThreadInfo * const threadInfo = findCurrentThreadInfo(); |
| _DBGUTILS_PLAIN_ASSERT(threadInfo); |
| |
| threadInfo->callOffset += offset; |
| |
| if ( threadInfo->callOffset == 0 ) |
| { |
| // thread call stack has dropped to zero, unregister it |
| android_atomic_acquire_store(0, &threadInfo->threadId); |
| } |
| } |
| |
| |
| inline int Debug::offsetForCurrentThread() |
| { |
| #ifdef TI_UTILS_FUNCTION_LOGGER_ENABLE |
| ThreadInfo * const threadInfo = findCurrentThreadInfo(); |
| _DBGUTILS_PLAIN_ASSERT(threadInfo); |
| |
| return threadInfo->callOffset; |
| #else |
| return 0; |
| #endif |
| } |
| |
| |
| inline void Debug::increaseOffsetForCurrentThread() |
| { |
| #ifdef TI_UTILS_FUNCTION_LOGGER_ENABLE |
| addOffsetForCurrentThread(1); |
| #endif |
| } |
| |
| |
| inline void Debug::decreaseOffsetForCurrentThread() |
| { |
| #ifdef TI_UTILS_FUNCTION_LOGGER_ENABLE |
| addOffsetForCurrentThread(-1); |
| #endif |
| } |
| |
| |
| inline void Debug::log(const int priority, const char * const format, ...) |
| { |
| va_list args; |
| va_start(args, format); |
| __android_log_vprint(priority, LOG_TAG, format, args); |
| va_end(args); |
| } |
| |
| |
| |
| |
| inline FunctionLogger::FunctionLogger(const char * const file, const int line, const char * const function) : |
| mFile(file), mLine(line), mFunction(function), mThreadId(androidGetThreadId()), mExitLine(-1) |
| { |
| Debug * const debug = Debug::instance(); |
| debug->increaseOffsetForCurrentThread(); |
| android_printLog(ANDROID_LOG_DEBUG, LOG_TAG, |
| TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s+ %s:%d %s - ENTER", |
| TI_UTILS_DEBUG_TIMESTAMP_VARIABLE |
| (int)mThreadId, IndentString<>(debug->offsetForCurrentThread()).string(), |
| mFile, mLine, mFunction); |
| } |
| |
| |
| inline FunctionLogger::~FunctionLogger() |
| { |
| Debug * const debug = Debug::instance(); |
| android_printLog(ANDROID_LOG_DEBUG, LOG_TAG, |
| TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s- %s:%d %s - EXIT", |
| TI_UTILS_DEBUG_TIMESTAMP_VARIABLE |
| (int)mThreadId, IndentString<>(debug->offsetForCurrentThread()).string(), |
| mFile, mExitLine == -1 ? mLine : mExitLine, mFunction); |
| debug->decreaseOffsetForCurrentThread(); |
| } |
| |
| |
| inline void FunctionLogger::setExitLine(const int line) |
| { |
| if ( mExitLine != -1 ) |
| { |
| Debug * const debug = Debug::instance(); |
| android_printLog(ANDROID_LOG_DEBUG, LOG_TAG, |
| TI_UTILS_DEBUG_TIMESTAMP_TOKEN "(%x) %s %s:%d %s - Double function exit trace detected. Previous: %d", |
| TI_UTILS_DEBUG_TIMESTAMP_VARIABLE |
| (int)mThreadId, IndentString<>(debug->offsetForCurrentThread()).string(), |
| mFile, line, mFunction, mExitLine); |
| } |
| |
| mExitLine = line; |
| } |
| |
| |
| |
| |
| } // namespace Ti |
| |
| |
| |
| |
| #endif //DEBUG_UTILS_H |