//返回当前信号灯的当前值, 很遗憾,WINDOWS下不支持,ReleaseSemaphore有类似功能,但是lReleaseCount参数不能为0
//如果用ReleaseSemaphore和WaitForSingleObject拼凑一个那么可能更超级糟糕,
//微软的API实现的真烂。Visual studio也承诺增加这个特性,但是在2010版本是不要做指望了。
int ZCE_OS::sem_getvalue(sem_t *sem, int *sval)
{
#if defined (ZCE_OS_WINDOWS)
    ZCE_UNUSED_ARG(sem);
    ZCE_UNUSED_ARG(sval);
    errno = EINVAL;
    return -1;
#elif defined (ZCE_OS_LINUX)
    return ::sem_getvalue(sem, sval);
#endif
}
//初始化读写锁对象
int ZCE_LIB::pthread_rwlock_initex(pthread_rwlock_t *rwlock,
                                   bool  use_win_slim,
                                   bool  priority_to_write)
{
    int result = 0;
    pthread_rwlockattr_t attr;

#if defined ZCE_OS_WINDOWS
    attr.priority_to_write_ = priority_to_write;
    //如果支持2008才能设置这个变量
#if defined ZCE_SUPPORT_WINSVR2008 && ZCE_SUPPORT_WINSVR2008 == 1
    rwlock->use_win_slim_ = use_win_slim;
#else
    rwlock->use_win_slim_ = false;
    ZCE_UNUSED_ARG(use_win_slim);
#endif

#elif defined ZCE_OS_LINUX

    ZCE_UNUSED_ARG(use_win_slim);
    ZCE_UNUSED_ARG(priority_to_write);

    //其实我在想,不如搞个NULL,其实都一样
    result = ::pthread_rwlockattr_init(&attr);

    if (result != 0)
    {
        return result;
    }

    result = ::pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);

    if (result != 0)
    {
        return result;
    }

#endif

    result = ZCE_LIB::pthread_rwlock_init(rwlock, &attr);

    if (result != 0)
    {
        return result;
    }

    return 0;
}
Beispiel #3
0
int test_hashtable(int /*argc*/ , char * /*argv*/ [])
{
    size_t numnode = 100,real_num=0;

    size_t szalloc = ZCE_LIB::shm_hashtable <int, int >::getallocsize(numnode, real_num);

    std::cout << "need mem: " << (int)szalloc << " num of node:" << (int)real_num << std::endl;
    std::cout << "sizeof smem_hashtable:" << sizeof(ZCE_LIB::shm_hashtable <int, int >) << std::endl;
    char *tmproom = new char [szalloc + 4];
    memset(tmproom, 0, szalloc + 4);

    ZCE_LIB::shm_hashtable<int, int >* pmmap = ZCE_LIB::shm_hashtable<int, int >::initialize(numnode, real_num,tmproom);
    pmmap->insert_unique(1001);
    ZCE_LIB::shm_hashtable<int, int >::iterator it = pmmap->find_value(1001);


    std::cout << "it serial: " << (int)(it.getserial()) << std::endl;

    bool bdel = pmmap->erase_unique(1001);

    it = pmmap->find_value(1002);

    bdel = pmmap->erase_unique(1001);

    std::cout << "it serial: " << (int)(it.getserial()) << std::endl;

    if (it == pmmap->end())
    {
        std::cout << "Not Fount." << std::endl;
    }
    ZCE_UNUSED_ARG(bdel);

    return 0;
}
Beispiel #4
0
 virtual void on_run(void *outer_data, bool &continue_run)
 {
     ZCE_UNUSED_ARG(outer_data);
     switch (get_stage())
     {
     case FMS1_STAGE_1:
         std::cout << "FSM1 stage " << get_stage() << " start."<< std::endl;
         continue_run = true;
         set_stage(FMS1_STAGE_2);
         break;
     case FMS1_STAGE_2:
         std::cout << "FSM1 stage " << get_stage() << std::endl;
         continue_run = true;
         set_stage(FSM1_STAGE_3);
         break;
     case FSM1_STAGE_3:
         std::cout << "FSM1 stage " << get_stage() << std::endl;
         continue_run = true;
         set_stage(FSM1_STAGE_4);
         break;
     case FSM1_STAGE_4:
         std::cout << "FSM1 stage " << get_stage() << " end."<<std::endl;
         continue_run = false;
         break;
     default:
         //一个无法识别的状态
         ZCE_ASSERT(false);
         break;
     }
     return;
 }
int ZCE_Event_INotify::rm_watch(ZCE_HANDLE watch_handle)
{
#if defined (ZCE_OS_LINUX)
    //先用句柄查询
    HDL_TO_EIN_MAP::iterator iter_del = watch_event_map_.find(watch_handle);
    if (iter_del == watch_event_map_.end())
    {
        return -1;
    }

    int ret =  ::inotify_rm_watch(inotify_handle_, iter_del->second.watch_handle_);
    if (ret != 0)
    {
        return -1;
    }

    //从MAP中删除这个NODe
    watch_event_map_.erase(iter_del);
    return 0;
#elif defined (ZCE_OS_WINDOWS)

    ZCE_UNUSED_ARG(watch_handle);
    if (watch_handle_ != ZCE_INVALID_HANDLE)
    {
        //从反应器移除
        reactor()->remove_handler(this, false);

        ::CloseHandle(watch_handle_);
        watch_handle_ = ZCE_INVALID_HANDLE;
        watch_path_[0] = '\0';
    }
    return 0;
#endif
}
//用于测试某些特殊情况的代码。
int test_timer_expire2(int /*argc*/, char * /*argv*/ [])
{
    ZCE_Timer_Queue_Base::instance(new ZCE_Timer_Wheel(1024));

    Test_Timer_Handler test_timer[10];
    int timer_id[10];
    ZCE_Time_Value delay_time(1, 0);
    ZCE_Time_Value interval_time(1, 0);
    for (size_t i = 0; i < TEST_TIMER_NUMBER; ++i)
    {
        delay_time.sec(i);
        timer_id[i] = ZCE_Timer_Queue_Base::instance()->schedule_timer(&test_timer[i],
            &TEST_TIMER_ACT[i],
            delay_time,
            interval_time);
    }

    //一些特殊情况下,定时器很长时间无法触发,导致的问题
    for (size_t j = 0; j < 100000; j++)
    {
        ZCE_LIB::sleep(60);
        ZCE_Timer_Queue_Base::instance()->expire();
        
    }
    ZCE_UNUSED_ARG(timer_id);
    return 0;
}
//打开(有名)信号灯,最大值不是标准参数,所以用默认只修饰了,这个主要用于创建有名信号灯,
//打开后,要使用sem_close,sem_unlink
sem_t *ZCE_OS::sem_open(const char *name,
                        int oflag,
                        mode_t mode,
                        unsigned int init_value,
                        unsigned int max_val)
{
#if defined (ZCE_OS_WINDOWS)

    ZCE_UNUSED_ARG (oflag);
    ZCE_UNUSED_ARG (mode);

    //为了保存变量还是先new出来
    sem_t *ret_sem = new sem_t ();
    ret_sem->sem_unnamed_ = false;

    HANDLE sem_handle = ::CreateSemaphoreA(NULL,
                                           init_value,
                                           max_val,
                                           name);

    if (sem_handle == 0)
    {
        delete ret_sem;
        ret_sem = NULL;
        return SEM_FAILED;
    }
    else
    {
        // Make sure to set errno to ERROR_ALREADY_EXISTS if necessary.
        errno = ::GetLastError ();

        ret_sem->sem_hanlde_ = sem_handle;
        return ret_sem;
    }

#elif defined (ZCE_OS_LINUX)
    //非标准参数
    ZCE_UNUSED_ARG(max_val);

    return ::sem_open (name,
                       oflag,
                       mode,
                       init_value);
#endif
}
//删除信号灯
int ZCE_OS::sem_unlink(const char *name)
{
#if defined (ZCE_OS_WINDOWS)
    ZCE_UNUSED_ARG (name);
    return 0;
#elif defined (ZCE_OS_LINUX)
    return ::sem_unlink(name);
#endif
}
Beispiel #9
0
//互斥量属性销毁
int ZCE_LIB::pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
{
#if defined (ZCE_OS_WINDOWS)
    ZCE_UNUSED_ARG(attr);
    return 0;
#elif defined (ZCE_OS_LINUX)
    return ::pthread_mutexattr_init (attr);
#endif
}
Beispiel #10
0
int ZCE_OS::pthread_condattr_destroy(pthread_condattr_t *attr)
{
#if defined (ZCE_OS_WINDOWS)
    ZCE_UNUSED_ARG(attr);
    return 0;
#elif defined (ZCE_OS_LINUX)
    return ::pthread_condattr_destroy (attr);
#endif
}
Beispiel #11
0
ZCE_Mysql_STMT_Command::~ZCE_Mysql_STMT_Command()
{
    if ( NULL  != mysql_stmt_)
    {
        int tmpret = ::mysql_stmt_free_result(mysql_stmt_);
        tmpret = ::mysql_stmt_close(mysql_stmt_);
        ZCE_UNUSED_ARG(tmpret);
    }
}
Beispiel #12
0
int test_nonr_thread_mutex(int, char * [])
{
    ZCE_Thread_NONR_Mutex  abc;
    abc.lock();
    int last_error = ZCE_LIB::last_error();
    abc.lock();
    last_error = ZCE_LIB::last_error();

    ZCE_UNUSED_ARG(last_error);
    return 0;
}
//对一个线程进行松绑
int ZCE_OS::pthread_detach(ZCE_THREAD_ID threadid)
{
#if defined (ZCE_OS_WINDOWS)
    //Windows线程本来就是detach的,呵呵
    ZCE_UNUSED_ARG(threadid);
    return 0;
#endif //#if defined (ZCE_OS_WINDOWS)

#if defined (ZCE_OS_LINUX)
    return ::pthread_detach (threadid);
#endif //#if defined (ZCE_OS_LINUX)
}
//初始化,创建一个无名(匿名)信号灯,对应的销毁函数sem_destroy
int ZCE_OS::sem_init(sem_t *sem,
                     int pshared,
                     unsigned int init_value,
                     unsigned int max_val)
{

#if defined (ZCE_OS_WINDOWS)

    //字符串长度为0表示是匿名信号灯
    sem->sem_unnamed_ = true;

    ZCE_UNUSED_ARG (pshared);

    HANDLE sem_handle = ::CreateSemaphoreA(NULL,
                                           init_value,
                                           max_val,
                                           NULL);

    if (sem_handle == 0)
    {
        return -1;
    }
    else
    {
        // Make sure to set errno to ERROR_ALREADY_EXISTS if necessary.
        errno = ::GetLastError ();
        sem->sem_hanlde_ = sem_handle;
        return 0;
    }

#elif defined (ZCE_OS_LINUX)
    //
    //非标准参数
    ZCE_UNUSED_ARG(max_val);
    return ::sem_init (sem,
                       pshared,
                       init_value);
#endif
}
int ZCE_LIB::exchage_coroutine(coroutine_t *save_hdl,
                               coroutine_t *goto_hdl)
{

#if defined ZCE_OS_WINDOWS
    //
    ZCE_UNUSED_ARG(save_hdl);
    ::SwitchToFiber(goto_hdl->coroutine_);
    return 0;

#elif defined ZCE_OS_LINUX
    return ::swapcontext(&save_hdl->coroutine_,
                         &goto_hdl->coroutine_);
#endif
}
Beispiel #16
0
int Comm_Timer_Handler::handle_timeout(const ZCE_Time_Value &now_time,
                                       const void *act /*= 0*/)
{
    ZCE_UNUSED_ARG(act);

    // 定时更新时间
    update_time(now_time);

    // reload配置
    notify_reload();

    // 处理监控
    check_monitor(now_time);

    return SOAR_RET::SOAR_RET_SUCC;
}
Beispiel #17
0
//初始化条件变量对象,不同的平台给不同的默认定义
//非标准,但是建议你使用,简单多了,
//如果要多进程共享,麻烦你老给个名字,同时在LINUX平台下,你必须pthread_condattr_t放入共享内存
int ZCE_LIB::pthread_cond_initex(pthread_cond_t *cond,
                                 bool win_mutex_or_sema)
{

    //前面有错误返回,
    int result = 0;

    pthread_condattr_t attr;
    result = ZCE_LIB::pthread_condattr_init (&attr);
    if (0 != result)
    {
        return result;
    }

    //这个是我在WINDOWS下用的,用于某些时候我可以用临界区,而不是更重的互斥量
#if defined (ZCE_OS_WINDOWS)
    //默认还是用递归的锁
    attr.outer_lock_type_ |= PTHREAD_MUTEX_RECURSIVE;

    if (win_mutex_or_sema)
    {
        attr.outer_lock_type_ |= PTHREAD_MUTEX_TIMEOUT;
    }

#elif defined (ZCE_OS_LINUX)
    ZCE_UNUSED_ARG(win_mutex_or_sema);

    result = ::pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
    if (0 != result)
    {
        return result;
    }
#endif

    result = ZCE_LIB::pthread_cond_init(cond, &attr);
    ZCE_LIB::pthread_condattr_destroy (&attr);

    if (0 != result)
    {
        return EINVAL;
    }

    return 0;
}
Beispiel #18
0
    int initialize(size_t data_number, bool if_restore)
    {
        int ret = 0;

        data_number_ = data_number;
        size_t malloc_size = HASH_TABLE_MY_DATA::getallocsize(data_number, data_number_);

        ret = mmap_file_.open("./LUX_DATA_.MMAP", malloc_size);

        hash_my_data_ = HASH_TABLE_MY_DATA::initialize(data_number,
            data_number_,
            (char *)(mmap_file_.addr()),
            if_restore);
        if (hash_my_data_)
        {
            return -1;
        }
        ZCE_UNUSED_ARG(ret);
        return 0;
    }
//超时处理
int Server_Timer_Base::timer_timeout(const ZCE_Time_Value &now_time,
                                     const void *act )
{
    ZCE_UNUSED_ARG(act);

    //记录当前时间,
    now_time_ = now_time;


    const int timeid = *(static_cast<const int *>(act));
    if (SERVER_TIMER_ID[0] == timeid)
    {
        ++heartbeat_counter_;
        heart_total_mesc_ = heartbeat_counter_ * heart_precision_.total_msec();

        // 处理监控
        check_monitor(now_time);
    }

    return 0;
}
//destructor 参数对于WIN32平台没有用,建议不用。
//
int ZCE_OS::pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
{
#if defined (ZCE_OS_WINDOWS)

    ZCE_UNUSED_ARG(destructor);

    *key = ::TlsAlloc ();

    //如果返回FALSE标识失败
    if (TLS_OUT_OF_INDEXES == *key)
    {
        errno = GetLastError();
        return -1;
    }

    return 0;

#elif defined (ZCE_OS_LINUX)
    //
    return ::pthread_key_create(key, destructor);
#endif
}
Beispiel #21
0
int test_lru_hashtable2(int /*argc*/ , char * /*argv*/ [])
{
    size_t numnode = 100, real_num = 0;
    size_t num_count = 0;

    size_t szalloc = ZCE_LIB::shm_hashtable_expire <int, int >::getallocsize(numnode, real_num);

    std::cout << "need mem: " << (int)szalloc << " num of node:" << (int)numnode << std::endl;
    std::cout << "sizeof :" << sizeof(ZCE_LIB::shm_hashtable_expire <int, int >) << std::endl;
    char *tmproom = new char [szalloc + 4];
    memset(tmproom, 0, szalloc + 4);

    //
    ZCE_LIB::shm_hashtable_expire<int, int >* pmmap = ZCE_LIB::shm_hashtable_expire<int, int >::initialize(numnode, real_num, tmproom);
    pmmap->insert_unique(1001, static_cast<unsigned int>(time(NULL)));
    pmmap->insert_unique(38636, static_cast<unsigned int>(time(NULL)));
    pmmap->insert_unique(36384378, static_cast<unsigned int>(time(NULL)));
    pmmap->insert_unique(65231237, static_cast<unsigned int>(time(NULL)));
    num_count  = pmmap->count(1001);

    ZCE_LIB::shm_hashtable_expire<int, int >::iterator it_tmp = pmmap->begin();
    ZCE_LIB::shm_hashtable_expire<int, int >::iterator it_end = pmmap->end();

    for (; it_tmp != it_end; ++it_tmp)
    {
        std::cout << "it_tmp value: " << (*it_tmp) << std::endl;
    }

    pmmap->active_unique(1001, static_cast<unsigned int>(time(NULL)));
    it_tmp = pmmap->begin();

    for (; it_tmp != it_end; ++it_tmp)
    {
        std::cout << "it_tmp value: " << (*it_tmp) << std::endl;
    }
    ZCE_UNUSED_ARG(num_count);

    return 0;
}
Beispiel #22
0
//设置线程的属性,不同的平台给不同的默认定义
//非标准,但是建议你使用,简单多了,
//如果要多进程共享,麻烦你老给个名字,同时在LINUX平台下,你必须pthread_mutex_t放入共享内存
int ZCE_LIB::pthread_mutex_initex(pthread_mutex_t *mutex,
                                  bool process_share,
                                  bool recursive,
                                  bool need_timeout,
                                  const char *mutex_name)
{

    //前面有错误返回,
    int result = 0;
    pthread_mutexattr_t attr;
    result = ZCE_LIB::pthread_mutexattr_init (&attr);

    if (0 != result)
    {
        return result;
    }

    int lock_shared = 0;
    if (process_share)
    {
        lock_shared = PTHREAD_PROCESS_SHARED;

        //进程共享,必须有个名字,WIN和LINUX都必须一样,LINUX下用名字创建共享内存
        if (mutex_name)
        {
#if defined (ZCE_OS_WINDOWS)
            strncpy(attr.mutex_name_, mutex_name, PATH_MAX);
#endif
        }
    }
    else
    {
        lock_shared = PTHREAD_PROCESS_PRIVATE;
    }

    //设置共享属性
    result = ZCE_LIB::pthread_mutexattr_setpshared(&attr, lock_shared);

    if (0 != result)
    {
        ZCE_LIB::pthread_mutexattr_destroy (&attr);
        return -1;
    }

    int lock_type = 0;

    if ( recursive )
    {
        lock_type |= PTHREAD_MUTEX_RECURSIVE;
    }

    //这个是我在WINDOWS下用的,用于某些时候我可以用临界区,而不是更重的互斥量
#if defined (ZCE_OS_WINDOWS)

    if (need_timeout)
    {
        lock_type |= PTHREAD_MUTEX_TIMEOUT;
    }

#else
    ZCE_UNUSED_ARG(need_timeout);
#endif

    //设置属性
    result = ZCE_LIB::pthread_mutexattr_settype(&attr, lock_type);

    if (0 != result)
    {
        ZCE_LIB::pthread_mutexattr_destroy (&attr);
        return result;
    }

    result = ZCE_LIB::pthread_mutex_init(mutex, &attr);
    ZCE_LIB::pthread_mutexattr_destroy (&attr);

    if (0 != result)
    {
        return result;
    }

    return 0;
}
Beispiel #23
0
int ZCE_LIB::backtrace_stack(ZCE_LOG_PRIORITY dbg_lvl,
                             const char *dbg_info)
{

    //跟踪函数的层数
    const size_t SIZE_OF_BACKTRACE_FUNC = 100;

#if defined(ZCE_OS_LINUX)
    ZCE_LOG(dbg_lvl, "[BACKTRACE]This program compiled by Linux GCC. %s", dbg_info);
    //Windows 下必须是2008或者VISTA之后的SDK才支持,
#elif defined(ZCE_OS_WINDOWS) && ZCE_SUPPORT_WINSVR2008 == 1
    ZCE_LOG(dbg_lvl, "[BACKTRACE]This program compiled by Windows Visual studio .%s", dbg_info);
#else
    ZCE_UNUSED_ARG(SIZE_OF_BACKTRACE_FUNC);
    ZCE_LOG(dbg_lvl, "[BACKTRACE]back_trace_stack don't support this system.%s", dbg_info);
#endif


    //这个方法是提供给Linux 下的GCC使用的
#if defined(ZCE_OS_LINUX)


    void *backtrace_buffer[SIZE_OF_BACKTRACE_FUNC];
    char **symbols_strings;

    int sz_of_stack = ::backtrace(backtrace_buffer, SIZE_OF_BACKTRACE_FUNC);

    //
    symbols_strings = ::backtrace_symbols(backtrace_buffer, sz_of_stack);

    if (symbols_strings == NULL)
    {
        ZCE_LOG(dbg_lvl, "%s", "[BACKTRACE] backtrace_symbols return fail.");
    }

    //打印所有的堆栈信息,有些时候信息无法显示符号表,建议使用
    for (int j = 0; j < sz_of_stack; j++)
    {
        ZCE_LOG(dbg_lvl, "[BACKTRACE] %u, %s.", j + 1, symbols_strings[j]);
    }

    //释放空间
    ::free(symbols_strings);

#elif defined(ZCE_OS_WINDOWS) && ZCE_SUPPORT_WINSVR2008 == 1

    //我还没有时间看完dbghelp所有的东西,目前的代码参考后一个版本居多,目前这个东东必须有pdb文件,
    //http://blog.csdn.net/skies457/article/details/7201185

    // Max length of symbols' name.
    const size_t MAX_NAME_LENGTH = 256;

    // Store register addresses.
    CONTEXT context;
    // Call stack.
    STACKFRAME64 stackframe;
    // Handle to current process & thread.
    HANDLE process, cur_thread;
    // Generally it can be subsitituted with 0xFFFFFFFF & 0xFFFFFFFE.
    // Debugging symbol's information.
    PSYMBOL_INFO symbol;
    // Source information (file name & line number)
    IMAGEHLP_LINE64 source_info;
    // Source line displacement.
    DWORD displacement;

    // Initialize PSYMBOL_INFO structure.
    // Allocate a properly-sized block.
    symbol = (PSYMBOL_INFO)malloc(sizeof(SYMBOL_INFO) + (MAX_NAME_LENGTH) * sizeof(char));
    memset(symbol, 0, sizeof(SYMBOL_INFO) + (MAX_NAME_LENGTH) * sizeof(TCHAR));
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);  // SizeOfStruct *MUST BE* set to sizeof(SYMBOL_INFO).
    symbol->MaxNameLen = MAX_NAME_LENGTH;

    // Initialize IMAGEHLP_LINE64 structure.
    memset(&source_info, 0, sizeof(IMAGEHLP_LINE64));
    source_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);

    // Initialize STACKFRAME64 structure.
    RtlCaptureContext(&context);            // Get context.
    memset(&stackframe, 0, sizeof(STACKFRAME64));

    // Fill in register addresses (EIP, ESP, EBP).

    stackframe.AddrPC.Mode = AddrModeFlat;
    stackframe.AddrStack.Mode = AddrModeFlat;
    stackframe.AddrFrame.Mode = AddrModeFlat;

#if defined ZCE_WIN32
    stackframe.AddrPC.Offset = context.Eip;
    stackframe.AddrStack.Offset = context.Esp;
    stackframe.AddrFrame.Offset = context.Ebp;
#elif defined ZCE_WIN64
    stackframe.AddrPC.Offset = context.Rip;
    stackframe.AddrStack.Offset = context.Rsp;
    stackframe.AddrFrame.Offset = context.Rbp;
#else
#endif
    // Get current process & thread.
    process = GetCurrentProcess();
    cur_thread = GetCurrentThread();

    // Initialize dbghelp library.
    if (!SymInitialize(process, NULL, TRUE))
    {
        return -1;
    }

    //这些空间是绝对足够的,我也不做详细的检查了
    const size_t LINE_OUTLEN = 1024;
    char line_out[LINE_OUTLEN];
    int use_len = 0;

    uint32_t k = 0;
    // Enumerate call stack frame.
    while (StackWalk64(IMAGE_FILE_MACHINE_I386,
                       process,
                       cur_thread,
                       &stackframe,
                       &context,
                       NULL,
                       SymFunctionTableAccess64,
                       SymGetModuleBase64,
                       NULL))
    {
        use_len = 0;
        // End reaches.
        if (stackframe.AddrFrame.Offset == 0 || k > SIZE_OF_BACKTRACE_FUNC)
        {
            break;
        }

        // Get symbol.
        if (SymFromAddr(process, stackframe.AddrPC.Offset, NULL, symbol))
        {
            use_len += snprintf(line_out + use_len, LINE_OUTLEN - use_len, " %s", symbol->Name);

        }

        if (SymGetLineFromAddr64(process, stackframe.AddrPC.Offset,
                                 &displacement,
                                 &source_info))
        {
            // Get source information.
            use_len += snprintf(line_out + use_len, LINE_OUTLEN - use_len, "\t[ %s: %d] at addr 0x % 08LX",
                                source_info.FileName,
                                source_info.LineNumber,
                                stackframe.AddrPC.Offset);
        }
        else
        {
            // If err_code == 0x1e7, no symbol was found.
            if (GetLastError() == 0x1E7)
            {
                use_len += snprintf(line_out + use_len, LINE_OUTLEN - use_len, "%s", "\tNo debug symbol loaded for this function.");
            }
        }
        ZCE_LOG(dbg_lvl, "[BACKTRACE] %u, %s.", k + 1, line_out);
        ++k;
    }

    SymCleanup(process);    // Clean up and exit.
    free(symbol);

#endif

    //
    return 0;

}