14 8月 2013

[Win32] 在C/C++中偵測精準的程式時間(Performance Time)

在Windows底下,要偵測精準的Perfromance Time,可以透過QueryPerformanceFrequency() & QueryPerformanceCounter() 來取得。

由於取得的時間存在LARGE_INTEGER的structure,要取出來計算所花費的時間需要多點程式碼才行。這邊寫了一個簡單的Wrapper Class,讓這個AutoQueryPerformance的instance life-time結束後,自行dump出所花費的時間。

AutoQueryPerformance

#include "windows.h"

// Auto dump the life-time of AutoQueryPerformance
// Ex:
// {
//     AutoQueryPerformance autoQuery("Test", true);
//     ...
// } // it will dump message when @autoQuery destroy
class AutoQueryPerformance
{
public:
    AutoQueryPerformance(const char* info, INT loop_count=1, BOOL autoDump=TRUE)
        : m_count_time(0), m_loop_count(loop_count), m_auto(autoDump)
    {
        m_info = info;
        ::ZeroMemory(&m_cpu_freq, sizeof(LARGE_INTEGER));
        ::QueryPerformanceFrequency(&m_cpu_freq);

        if( autoDump )
            begin();
    }
    ~AutoQueryPerformance()
    {
        pause();
        if( autoDump )
            printf("[AutoQueryPerformance][%s] spend time=%.4f", m_info.c_str(), getTime());
    }
    inline void begin()
    {
        ::ZeroMemory(&m_start_clock, sizeof(LARGE_INTEGER));;
        ::QueryPerformanceCounter(&m_start_clock);
    }
    inline void pause()
    {
        ::ZeroMemory(&m_cur_clock, sizeof(LARGE_INTEGER));
        ::QueryPerformanceCounter(&m_cur_clock);
        m_count_time += m_cur_clock.QuadPart-m_start_clock.QuadPart;
    }
    inline void stop()
    {
        pause();
        m_count_time = 0;
    }
    inline float getTime()
    {
        return (m_count_time/(float)m_cpu_freq.QuadPart) / m_loop_count;
    }
private:
    LARGE_INTEGER m_cpu_freq;
    LARGE_INTEGER m_start_clock;
    LARGE_INTEGER m_cur_clock;
    LONGLONG      m_count_time;
    INT           m_loop_count;//for for-loop to calculate correct average time
    std::string   m_info;
    BOOL          m_auto;
};
#define AUTO_QUERY_PERFORMANCE(x) AutoQueryPerformance __at_qp = AutoQueryPerformance(x);


使用範例

void TestingTotalTime(int loop_count)
{
    AutoQueryPerformance ap("TestingTotalTime");
    int sum = 0;
    for(int i=0 ; i<loop_count; i++)
       sum += i;
}

void TestingSingleTime(int loop_count)
{
    AutoQueryPerformance ap("TestingSingleTime", loop_count);
    for(int i=0 ; i<loop_count; i++)
       //HeavyFunction call here;
}

Reference

12 8月 2013

[Win32/COM] Use COM DLL without registered

依照Windows COM元件的使用方法,應該要將COM DLL註冊後,即可在程式中,以CoCreateInstance()帶入CLSID、IID就可以將該DLL自動load起來,創建該class的instance了。

但是在開發時,實際上總是沒這麼美好,註冊DLL其實是很麻煩的,新舊相容性,開發中的版本等等的問題,而走manifest其實可以避開註冊這條路,但是今天我需要寫一個COM module讓別人使用,而這個COM module在AP裡是走manifest的,所以不會被註冊,連帶的module會用到其他的Library也不走註冊這條路,而因為一些其他的原因,也不能將此Library寫入manifest裡,只能尋求其他途徑了。

由於COM的DLL都會link到一些基本的COM Library,所以是有一些export的API可以繞過註冊來create COM instance的,直接參考以下的Sample Code吧。


// {599D2F5F-BA8D-4009-941C-A5393EBD5C58}
DEFINE_GUID(CLSID_MyCOM, 
0x599d2f5f, 0xba8d, 0x4009, 0x94, 0x1c, 0xa5, 0x39, 0x3e, 0xbd, 0x5c, 0x58);

// {4C452CB2-469C-4236-AE5C-F48A28EC0870}
DEFINE_GUID(IID_IMyCOM, 
0x4c452cb2, 0x469c, 0x4236, 0xae, 0x5c, 0xf4, 0x8a, 0x28, 0xec, 0x8, 0x70);

HRESULT CreateInstance(IMyCOM** pMyCom)
{
    HMODULE hDll = LoadLibrary("MY_COM.dll");//Need refine by your self
    if(hDll)
    {
        //////////////////////////////////////////////////////////////////////////
        // Initial COM instance without register
        //   Try to load COM DLL directly, and call DllGetClassObject to get IID_IClassFactory
        //   And Create COM instance by IID_IClassFactory
        // -----------------------------------------------------------------------
        //   HRESULT hr = CoCreateInstance(CLSID_MyCOM, 
        //                                 NULL, 
        //                                 CLSCTX_INPROC_SERVER, 
        //                                 IID_IMyCOM, 
        //                                 (PVOID*)&pMyCom);
        //////////////////////////////////////////////////////////////////////////
        CComPtr<IClassFactory> spFactory;

        // Get address of DllGetClassObject exported method
        PDLLGETCLSOBJ pDllGetCLSObj = (PDLLGETCLSOBJ)GetProcAddress(hDll, "DllGetClassObject");

        // Call DllGetClassObject and get the proper IClassFactory interface
        HRESULT hr = pDllGetCLSObj(CLSID_MyCOM, IID_IClassFactory, reinterpret_cast<PVOID*>(&spFactory));
        if( FAILED(hr) )
        {
            printf(__FUNCTION__" [Err] Can't get spFactory hr=%X", hr);
            return E_FAIL;
        }

        // Create an instance of IMyCOM
        hr = spFactory->CreateInstance(NULL, IID_IMyCOM, (PVOID*)pMyCom);
        if( FAILED(hr) )
        {
            printf(__FUNCTION__" [Err] Init IMyCOM Fail! hr=%X", hr);
            return E_FAIL;
        }
    }
    else
    {
        return E_FAIL;
    }
    return S_OK;
}


Reference:

[Python] 使用objgraph找出memory leak(circular reference)

在Python的世界裡,所有的東西都是Object,而控制Object的life cycle靠的就是reference的機制,一個簡單的assignement (=等號),或是function call就會讓Object的reference count自動加1。
而當Object的reference count減至0的時候,Python的GC會找時間把這些Object清除掉。

對Python而言,memory leak產生的可能性有兩種
  • Circular reference
  • Referece在global的變數或是module上

18 7月 2013

[Jython] Use JNA Read/Write Windows Registry

最近用Sikuli開發,Run Script時需要讀寫Windows的Registry,由於Sikuli script是用Jython的,而Jython裡並不像Python支援Read/Write Windows Registry,所以必須用一些其他的Library才行

而其中一個方法就是用JNA
下載最新的JNA與JNA-Platform
放到要執行的Script旁,或是放到Jython的目錄裡

以下的範例,將jna-3.5.1.jar與jna-platform-3.5.1.jar放到Sample.py旁,以Jython執行
Sample.py
import os
import sys
folder = os.path.dirname(os.path.abspath(__file__))

# load JavaLib jna/platform
sys.path.append(os.path.join(folder, 'jna-3.5.1.jar'))
sys.path.append(os.path.join(folder, 'jna-platform-3.5.1.jar'))

# Read
from com.sun.jna.platform.win32 import Advapi32Util, WinReg
path = Advapi32Util.registryGetStringValue( \
    WinReg.HKEY_LOCAL_MACHINE, 
    r'Software\CyberLink\ColorDirector2', 
    'InstallPath' )

print path.encode(sys.getfilesystemencoding())


Reference

17 7月 2013

[Python] Windows底下 使用SWIG呼叫C/C++的function

使用Python的script language的特性,開發起來實在是又快又舒服,但是Python有個致命的缺點就是GIL,在Multi-Thread的機制下,有了這個GIL的限制,感覺Multi-Thread就被俺掉一半了。照理來說heavy的blocking code應該要寫在另一條thread,並且放掉GIL,讓其他的thread有時間去做事才對。透過Python轉call進C/C++的function後,才有機會讓heavy的function放掉Python的GIL。

不過,本篇的重點在於介紹如果讓Python呼叫C/C++ level的function,有機會的話,再介紹一下GIL好了。目前其實有很多Tool提供這樣的功能,包括SWIG、Boost.Python、Robin…等等,這邊就介紹一下SWIG的用法。

25 6月 2013

[PHP] Debug PHP by Eclipse with PDT PlugIn

強大的Eclipse開發社群,提供了PHP的debug功能 - PHP Development Tool (PDT) PlugIn,可以執行並下中斷點,對PHP開發者而言著實是一大福音!

PDT套件可以視為是debug的client端,而server端的話,就需要在PHP安裝extension才行,目前PDT支援的包括(1)Zend Debugger (2)XDebug,以下就介紹PDT以Zend Debugger的方式進行debug ...


06 6月 2013

[Sikuli] Compile Sikuli-IDE source code in Eclipse

Sikuli是新堀起的Programming Language,以圖象化的方式來coding真的是一大圖破創新的想法。重點是...還是台灣人做的呢!Sikuli是Open Source的,所以有不少人為這個Project供獻一己之力,最近公司的Project用它來做AutoTest,整個很給力,決定來compile它的source code來玩看看,雖然說很久沒寫Java了.... XD

Sikuli的Project切成兩部分,一個是整個核心的Sikuli-API是以Java、C++為主,另一個是開發平台的Sikuli-IDE是純Java的Project,由於整個核心API切出來成一個單獨的Project,所以如果想自己寫一個Java的程式直接使用Sikuli-API是可行的!詳見:How to use Sikuli Script in your Java Program?

也由於Sikuli API模組化的關係,目前Sikuli Script語法是Python,以後可能還會陸續支援其他語言吧。

25 5月 2013

[C/C++] 研究long long * float overflow 可能導致的誤差

最近看bug看到一個奇怪的現象,在64bit的AP執行時,long long * float會有誤差出現,但是32bit的AP卻沒有這樣子的問題出現,直覺上不是應該32bit的AP比較會有問題嗎!?

先來看一下 long long 跟 float ,long long的size是8個byte,float的size是4個byte
long long * float,compiler會將結果存到float的temp變數裡,此時誤差就可能會出現了,但是這個誤差是「有條件」地出現,並不是任何long long的數字乘上float都會產生誤差的

以一個簡單的程式來驗證是否會產生誤差
bool check_longlong(long long v1)
{
    float f = 1.0f;
    long long v2 = long long( v1 * f );
    printf("ori value=%lld, new value=%lld, ori==new=%d", v1, v2, v1==v2);
    return v1==v2;
}

int _tmain(int argc, _TCHAR* argv[])
{
    check_longlong(0xF0000000000);//true - OK
    check_longlong(0x0FFFFFFFFFF);//false - Error!
    check_longlong(0x0FFFFFF);//true - OK
    check_longlong(0x1FFFFFF);//false - Error!
    return 0;
}


24 5月 2013

[C/C++] 使用STL時,混用Debug & Release Mode的DLL or LIB可能會造成 Crash!

當你的Release mode的EXE,使用Debug mode的DLL,中間如果有操作(new/delete)STL的container,很容易讓AP crash,包括下列的error ...
  • Debug Assertion Failed
  • _BLOCK_TYPE_IS_VALID
  • _CrtIsValidHeapPointer

[Python] 為Python2.5 unittest 加上setUpClass & tearDownClass 的功能

最近使用 Sikuli 替Project作AutoTest的功能,以Python的unittest為架構下去撰寫,發現有些綁手綁腳的地方,因為Sikuli是使用Jython 2.5,連帶的unittest也止於Python 2.5的版本。由於測試的項目眾多,需要撰寫多個TestCase,所以setUpClasstearDownClass更顯得重要,可以為每個TestCase做pre-process與post-process。偏偏setUpClass與tearDownClass是在Python 2.7後才支援的,所以想在Sikuli裡套用的話,必須自己加一些Code才行了。

23 5月 2013

[C/C++] 取得LIB目前的DLL module path

如果寫了一個LIB or Cpp檔,讓多個DLL module include使用,該如果拿到目前DLL的module path?

GetModuleFileName() 參數hModule傳NULL的話,只能知道執行檔exe的路徑
要想知道自己在哪個DLL module的話,必須埋一些code才行

EXTERN_C IMAGE_DOS_HEADER __ImageBase
//...
WCHAR dll_path[MAX_PATH] = {0};
GetModuleFileNameW((HINSTANCE)&__ImageBase, dll_path, _MAX_PATH);

Reference :

22 3月 2013

[C/C++] General pointer type for support x86 & x64

一個pointer的size在x86跟x64的環境裡是不一樣大的

  • x86's pointer size = 4 bytes
  • x64's pointer size = 8 bytes
如果你在程式中用int or long去儲存一個pointer的話,可能在compile成x86可以work,但是compile成x64執行的話,可能就會exception了

所以建議是用標準的 uintptr_t / intptr_t 去儲存pointer,uintptr_t在copmpile成x86時的size是4 bytes,x64是8 bytes,它在C++11時才被列入標準,但在大部分的C++03的compiler已經支援了

include header : <cstdint>

Reference : 

10 3月 2013

[wxWidgets] function 傳入wxString的不定參數

在C/C++裡有不定參數的功能,讓一個function可以傳入不固定數目的參數,在function裡,透過va_list, va_arg() 把參數一個個抓出來。

在wxWidgets裡,擁有特有的字串類別wxString,用法跟std::string, std::wstring稍稍不同(多了很多API)。

std::string/std::wstring可以透過c_str()取得的內部字串buffer (char*/wchar_t*)
wxString也可以透過c_str()取得內部字串的buffer,型別是wxChar

wxChar是wxWidgets定義的,會根據compile定義的wxUSE_UNICODE(是否為unicode的build)分別對應到unicode : wchar_t, ansi : char

由於wxString是wxWidgets包裝起來的class,若將它傳進function當不定參數之一,va_arg()將無法取得wxString正確的字串內容,所以如果要將wxString的字串傳入不定參數的function中,需以c_str()傳入wxChar*字串的pointer,才能在function中取得字串的內容。

// the parameter must be wxChar*
// -> please convert wxString by c_str() before push it
//    _T("") would be convert to wxChar*
// Ex: wxString s = _T("Falldog");
//     ComposeContent(2, s.c_str(), _T("'s blogger"));
wxString ComposeString(int count, ...)
{
    wxString res;
    va_list vl;
    va_start(vl, count);
    for(int i=0 ; i<count ; ++i)
        res += va_arg(vl,wxChar*);
    va_end(vl);
    return res;
}

30 8月 2012

[Code::Blocks] Compile Code::Blocks source code in Windows

Code::Blocks是一款出色的跨平台IDE,是用跨平台的語言wxWidgets所寫的,由於wxWidgets並不像Java一樣,有Virtual Machine,是可以build成原生的執行檔執行的,所以執行的速度會快很多。由於最近想寫看看Code::Blocks的PlugIn,所以就試著build CB的source code看看,用CB來build CB倒是挺有趣的。

23 8月 2012

[VisualStudio] Generate Assembly result file

VisualStudio在compile檔案過後,預設是不會產生assembly file的,但是有時候會想看看compile過後assembly code長怎麼樣,當project的Optimization有enabled的話,一樣的code在不同的Optimization level會有不一樣的結果,這就是VC對code優化處理。

要打開assember output的話,先進入Project Property Page(在project上點右鍵,選propertys)
以VS2008為例,在 Configuration Properties | C/C++ | Output Files
選擇 Assembler Output ,有一些選項可以選

  • Assembly-Only   (輸出.asm檔)
  • Assembly, Machine Code and Source (輸出.cod檔)
  • Assembly With Machine Code  (輸出.cod檔)
  • Assembly With Source Code  (輸出.asm檔)
可以選擇最詳細的Assembly, Machine Code and Source來看



最後就可以在指定的輸出路徑(ASM List Location)裡,找到各個檔案的*.asm or *.cod,打開來就可以研究一下裡面的assembly code了~

更多詳細的設定介紹 - MSDN - /Fa /FA