2011-09-20

How to profile JIT code with CodeAnalyst

CodeAnalyst is a good profiler tool.

It is easy to use, but it is difficult to analyze JIT code in standard way.
For example, let's try the following JIT sample code with Xbyak.

// a sample for JIT code
// cl t.cpp /EHsc /Zi /Ox
#include <xbyak/xbyak.h>
struct Code : public Xbyak::CodeGenerator {
    Code()
    {
        mov(eax, 1000000);
    L("@@");
        for (int i = 0; i < 10; i++) {
            sub(eax, 1);
        }
        jg("@b");
        mov(eax, 1);
        ret();
    }
};

struct Code2 : public Xbyak::CodeGenerator {
    Code2()
    {
        mov(eax, 1000000);
    L("@@");
        for (int i = 0; i < 10; i++) {
            xorps(xm0, xm0);
        }
        sub(eax, 1);
        jg("@b");
        mov(eax, 1);
        ret();
    }
};

int main()
{
    Code c;
    Code2 c2;
    int (*f)() = (int (*)())c.getCode();
    int (*g)() = (int (*)())c2.getCode();
    double sum = 0;
    for (int i = 0; i < 20000; i++) {
        sum += s1(i);
        sum += s2(i);
    }
    printf("sum=%f\n", sum);
    for (int i = 0; i < 2000; i++) {
        sum += f();
    }
    printf("f=%f\n", sum);
    for (int i = 0; i < 2000; i++) {
        sum += g();
    }
    printf("g=%f\n", sum);
}

I get the result of profile by CodeAnalyst.


The module t.exe is target program and in addtion, there is an unknown module pid(4972). We can't get more information about it if we click it.



The profiler can't analyse JIT code because there is no symbol information about it.

AMD provides an API to deal with JIT code, so let's use it. To my regret, there is no documents for API, so I fumble with it. The version of CodeAnalyst I used is 3.2.962.731.

At first, include CAJITNTFLib.h in the <installed directory>/CodeAnalyst/API/include. Three functions are defined in the header.
  1. Initialization
    CAJIT_Initialize();
  2. Register
    CAJIT_LogJITCode(); Specify the address and the size of JIT code.
  3. Termination
    CAJIT_CompleteJITLog();

Here is a sample code with these APIs.

 
#include <stdio.h>
#include <math.h>
#include <xbyak/xbyak.h>

#ifdef _WIN64
#define AMD64
#include "CAJITNTFLib.h"
#pragma comment(lib, "CAJitNtfyLib.lib")

struct Code : public Xbyak::CodeGenerator {
    Code()
    {
        mov(eax, 1000000);
    L("@@");
        for (int i = 0; i < 10; i++) {
            sub(eax, 1);
        }
        jg("@b");
        mov(eax, 1);
        ret();
    }
};

struct Code2 : public Xbyak::CodeGenerator {
    Code2()
    {
        mov(eax, 1000000);
    L("@@");
        for (int i = 0; i < 10; i++) {
            xorps(xm0, xm0);
        }
        sub(eax, 1);
        jg("@b");
        mov(eax, 1);
        ret();
    }
};

double s1(int n)
{
    double r = 0;
    for (int i = 0; i < n; i++) {
        r += 1.0 / (i + 1);
    }
    return r;
}

double s2(int n)
{
    double r = 0;
    for (int i = 0; i < n; i++) {
        r += 1.0 / (i * i + 1) + 2.0 / (i + 3);
    }
    return r;
}

int main()
{
    Code c;
    Code2 c2;
    int (*f)() = (int (*)())c.getCode();
    int (*g)() = (int (*)())c2.getCode();
    printf("f:%p, %d\n", f, c.getSize());
    printf("g:%p, %d\n", g, c2.getSize());

    CAJIT_Initialize();
    CAJIT_LogJITCode((size_t)f, c.getSize(), L"f");
    CAJIT_LogJITCode((size_t)g, c2.getSize(), L"g");

    double sum = 0;
    for (int i = 0; i < 20000; i++) {
        sum += s1(i);
        sum += s2(i);
    }
    printf("sum=%f\n", sum);
    for (int i = 0; i < 2000; i++) {
        sum += f();
    }
    printf("f=%f\n", sum);
    for (int i = 0; i < 2000; i++) {
        sum += g();
    }
    printf("g=%f\n", sum);
    CAJIT_CompleteJITLog();
    puts("end");
}

Build this code with correct path of include and lib of API, then I get the following result.






"JIT Code" Process Name appeared in stead of unknown module pid and I get the name "f" and "g" symbols.


We can see the generated JIT code if clicking the symbols!
(The snapshot is for Code2)