397 lines
12 KiB
C++
397 lines
12 KiB
C++
/*
|
|
* 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
|