응용프로그램에서 IO 읽기/제어

Posted at 2009/04/07 19:02 // in Programming // by Daniel

/dev/mem에 mmap을 쓰면 시스템의 물리 메모리에 접근 가능합니다.

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>

 

#define REALVIEW_SYS_BASE                   0x10000000
#define REALVIEW_SYS_100HZ_OFFSET           0x24
#define REALVIEW_SYS_24MHz_OFFSET           0x5C

int main( void )
{
    int fd;
    unsigned int *mapped_addr;
    unsigned int *p_sys24mhz;
    unsigned int *p_sys100hz;

    fd = open("/dev/mem", O_RDONLY | O_SYNC);
    if(fd==-1) {
        perror("open");
        return 1;
    }
    mapped_addr = (unsigned int *)mmap(0, 0x10000,
            PROT_READ, MAP_SHARED, fd, REALVIEW_SYS_BASE);
    //if (mapped_addr<0)
    //  perror("mmap");
    p_sys24mhz = mapped_addr+(REALVIEW_SYS_24MHz_OFFSET/sizeof(unsigned int));
    p_sys100hz = mapped_addr+(REALVIEW_SYS_100HZ_OFFSET/sizeof(unsigned int));
    printf("mapped addr= %p\n", mapped_addr);
    printf("p_sys100hz = %p\n", p_sys100hz);
    printf("*p_sys100hz = 0x%x\n", *p_sys100hz);
    printf("p_sys24mhz = %p\n", p_sys24mhz);
    printf("*p_sys24mhz = 0x%x\n", *p_sys24mhz);
    close(fd);
    return 0;
}

 

[root@target2 ~]# ./mmaptest
mapped addr= 0x40158000
p_sys100hz = 0x40158024
*p_sys100hz = 0x396fe45
p_sys24mhz = 0x4015805c
*p_sys24mhz = 0x778161b0

 

이렇게 쓰면 됩니다.
/dev/mem을 open하고 mmap을 이 fd에 대해 하면 됩니다.

크리에이티브 커먼즈 라이센스
Creative Commons License

__builtin_expect 키워드

Posted at 2009/04/01 22:08 // in Programming // by Daniel

재밌는 컴파일러 키워드 __builtin_expect

gcc에서만 일단 사용이 가능하지만 한가지 흥미있는 키워드를 발견했다. __builtin_expect 라는 키워드다 다음과 같이 사용할 수 있다.

void* p = malloc(100);

// if (p == NULL)
// error();

if (__builtin_expect(p == NULL, 0))
error();

위와 같은 경우는 아주 많이 접할 것이다. 그런데 사실 포인터 p가 NULL인 경우는 잘 없다. 99.99%는 항상 p != NULL가 될 것이다. 이럴 경우 __builtin_expect는 매우 유용하게 사용할 수 있다.
이 키워드는 p == NULL의 값이 *대부분* false가 될 것이 라고 컴파일러에게 알려준다. 그래서 CPU가 좀 더 효율적으로 명령어를 fetch할 수 있도록 한다. 물론 그렇다고해서 프로그램의 정확성, 즉 p가 NULL이면 error가 실행되라는 것까지 해치지는 않는다. 단순히 프로그램의 성능 향상을 위한 키워드이다.

pthread 소스 보면서 찾았네요.

pthread_mutex_lock 소스가 상당히 기네요..

glibc-2.7/nptl/pthread_mutex_lock.c

int
__pthread_mutex_lock (mutex)
     pthread_mutex_t *mutex;
{
  assert (sizeof (mutex->__size) >= sizeof (mutex->__data));
  int oldval;
  pid_t id = THREAD_GETMEM (THREAD_SELF, tid);
  int retval = 0;
  switch (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex),
                PTHREAD_MUTEX_TIMED_NP))
    {
      /* Recursive mutex.  */
    case PTHREAD_MUTEX_RECURSIVE_NP:
      /* Check whether we already hold the mutex.  */
      if (mutex->__data.__owner == id)
    {
      /* Just bump the counter.  */
      if (__builtin_expect (mutex->__data.__count + 1 == 0, 0))
        /* Overflow of the counter.  */
        return EAGAIN;
      ++mutex->__data.__count;
      return 0;
    }
      /* We have to get the mutex.  */
      LLL_MUTEX_LOCK (mutex);
      assert (mutex->__data.__owner == 0);
      mutex->__data.__count = 1;
      break;
      /* Error checking mutex.  */
    case PTHREAD_MUTEX_ERRORCHECK_NP:
      /* Check whether we already hold the mutex.  */
      if (__builtin_expect (mutex->__data.__owner == id, 0))
    return EDEADLK;
      /* FALLTHROUGH */

.......

 

이게 LinuxThreads와 NPTL의 소스가 다 있어서 한참을 검색했는데

이렇게 하면 나오네요. NPTL을 쓰는군요

$ /lib/libpthread.so.0
Native POSIX Threads Library by Ulrich Drepper et al
Copyright (C) 2006 Free Software Foundation, Inc.

 

데비안에서 소스 받기는

$ dpkg-query -S /lib/libpthread.so.0
libc6: /lib/libpthread.so.0
이렇게 찾고


$ apt-get source libc6
이렇게 받습니다.
압축은 푸셔야 되구요. -b 로 빌드도 같이 되게 할 수 있지만 의존성 때매 실패할겁니다

크리에이티브 커먼즈 라이센스
Creative Commons License

arm linux 커널에 system call 추가

Posted at 2009/04/01 17:01 // in Programming // by Daniel

http://barriosstory.blogspot.com/2008/03/arm-linux-kernel-system-call.html

먼저 커널의 다음 3 파일을 수정하여 새로운 system call을 추가하자.
1. arch/arm/kernel/calls.S
2. include/asm-arm/unistd.h
3. 해당 system call을 구현 할 부분(따로 분리해서 구현해도 상관없고 기존의 아무 소스 파일에나 구현해도 상관없다. 단지 makefile만 잘 수정해준다면)

 

마지막으로 해당 system call을 사용하는 application을 작성하여야 한다.

 

http://www.4ellene.net/tt/1168

system call 추가 방법
1. vi /usr/src/linux/arch/i386/kernel/entry.S
: system call 번호를 추가
.long SYMBOL_NAME(sys_mysyscall)  // 240

2. vi /usr/src/linux/include/asm-i386/unistd.h
#define __NR_mysyscall      240

3. vi /usr/src/linux/kernel/test.c
#include <linux/kernel.h>

asmlinkage int sys_mysyscall()
{  
    printk("Hello linux\n");
    return 2007;
}

4. vi /usr/src/linux/kernel/Makefile
obj-y = sched.o dma.o fork.o exec_domain.o panic.o printk.o \
        module.o exit.o itimer.o info.o time.o softirq.o resource.o \
        sysctl.o acct.o capability.o ptrace.o timer.o user.o \
        signal.o sys.o kmod.o context.o test.o 

5. vi app.c

#include <asm-i386/unistd.h>
#include <errno.h>

_syscall0( int, mysyscall );

int main()
{
    int i;
    i = mysyscall();

    printf("%d\n", i );
    return 0;
}

6. cc -I/usr/src/linux/include app.c

7. 커널 컴파일

http://kldp.org/node/87344

시스템 콜을 추가하는 하고자 하는데 잘안되네요
시스템 콜 함수까지는 제대로 구현되는 거는 같은데
그것을 test하는 user application이 잘되지 않네요
_syscall2(int, pedagogictime, int, flag, struct timeval *, thetime);

이런식으로 해주고 user application을 작성해서 컴파일 해주면
'__NR_pedagogictime' undeclared라고 나오네요
분명히 include/asm/arch/unistd.h에서 추가해줬는데 말입니다.

그런데 여러 자료를 보니까 include/asm-arm/unistd.h를 변경해준다고 하고
그런데 거기에 include에 가보니까 asm~~이런 것들이 굉장히 많은데
이것들의 차이는 무언가요 제가 맞게 바꾼거는 맞나요??

 

혹시 arch디렉토리가 없지 않나요?
인텔칩을 사용한다면 include/asm-i386/unistd.h 파일을 수정해주세요.
저 파일에

#define __NR_eventfd            323
#define __NR_pedagogictime            324
#ifdef __KERNEL__

#define NR_syscalls 325

라고 굵은 글씨로 되어 있는 부분을 입력해주시면 됩니다.

asm-???? 들은 각 CPU별로 어셈블리어가 다르기 때문에 CPU별로 나뉘어 있는 것입니다.

크리에이티브 커먼즈 라이센스
Creative Commons License

함수의 리턴 주소 알아내기

Posted at 2009/03/23 11:16 // in Programming // by Daniel

프로파일링이나 함수 래핑으로 lock등의 작업을 가로채서 로깅할 때 호출한 쪽의 PC값을 알고 싶을 경우가 있습니다.

현재실행되는 함수 입장에서 보면 리턴 주소이죠

이 때 쓸 수 있는 함수가 gcc의 builtin함수로 __builtin_return_address() 입니다.

__builtin_return_address(level)

여기서 level은 call stack 에서 몇번째를 리턴할 것인지에 대한 값입니다.

__builtin_return_address(0) 하면 현재함수의 리턴주소가 나옵니다.

__builtin_return_address(1) 하면 나를 호출한 함수의 리턴주소가 나옵니다...

예제 코드를 짜봤습니다.

#include <stdio.h>
#include <stdlib.h>

int my_func1()
{
    void *addr;
    addr = __builtin_return_address(0);
    printf("1's caller %p\n",addr);
    printf("hello 1\n");
}
int my_func0()
{
    void *addr;
    printf("hello 0\n");
    addr = __builtin_return_address(0);
    printf("0's caller %p\n",addr);
    my_func1();
}

int main(void)
{
    printf("backtrace test\n");
    printf("main: %p\n", (void *)main);
    printf("my_func0: %p\n", (void *)my_func0);
    printf("my_func1: %p\n", (void *)my_func1);
    my_func0();
    return 0;
}


이 소스코드를 컴파일해서 objdump해보면

080483d1 <my_func0>:
...
80483fc:   e8 a3 ff ff ff          call   80483a4 <my_func1>
8048401:   c9                      leave
8048402:   c3                      ret

08048403 <main>:
...

804845c:   e8 70 ff ff ff          call   80483d1 <my_func0>
8048461:   b8 00 00 00 00          mov    $0x0,%eax
...

이와 같이 호출하게 되어있고

실행해보면

backtrace test
main: 0x8048403
my_func0: 0x80483d1
my_func1: 0x80483a4
hello 0
0's caller 0x8048461
1's caller 0x8048401
hello 1

이렇게 나옵니다. call 바로 다음 주소--리턴 주소가 나오지요.

이외에도 몇가지 builtin함수들이 있습니다.

예를 들어 __builtin_frame_address(level)은 프레임 포인터를 반환합니다.


관련 포스트 :

gcc 컴파일 옵션으로 커스텀 프로파일 해서 실행 트레이스 뽑기

크리에이티브 커먼즈 라이센스
Creative Commons License

리눅스 커널 메모리 할당

Posted at 2008/12/03 15:55 // in Programming // by Daniel
커널 메모리 할당

· kmalloc의 실제 동작


· Lookaside Caches


· vmalloc과 관련함수


· Boot-Time Allocation



크리에이티브 커먼즈 라이센스
Creative Commons License

리눅스에서 태스크가 특정 코어에서 동작하게 하기 (affinity 설정)

Posted at 2008/11/27 16:07 // in Programming // by Daniel

커널 2.6부터 지원됩니다.


프로세스는 sched_setaffinity()가 있습니다.

쓰레드는 pthread_setaffinity_np()가 있습니다

커널의 소스에 보면 migration하는 경우에도 cpu affinity를 보고 설정된 CPU가 아니면 하지 않습니다.

그러므로 …_setaffinity()를 호출하면 해당 CPU에 고정되는 것이 맞습니다.

Hotplug시 affinity 설정된 모든 CPU가 꺼진 경우만 예외적으로 affinity를 변경합니다.

[code]
static void sched_migrate_task(task_t *p, int dest_cpu)
{
    migration_req_t req;
    runqueue_t *rq;
    unsigned long flags;

    rq = task_rq_lock(p, &flags);
    if (!cpu_isset(dest_cpu, p->cpus_allowed)
        || unlikely(cpu_is_offline(dest_cpu)))
        goto out;
....
static
int can_migrate_task(task_t *p, runqueue_t *rq, int this_cpu,
             struct sched_domain *sd, enum idle_type idle,
             int *all_pinned)
{
    /*
     * We do not migrate tasks that are:
     * 1) running (obviously), or
     * 2) cannot be migrated to this CPU due to cpus_allowed, or
     * 3) are cache-hot on their current CPU.
     */
    if (!cpu_isset(this_cpu, p->cpus_allowed))
        return 0;
[/code]

아래 코드를 2.6.24, x86, 4코어에서 테스트했습니다.

( http://www.thinkingparallel.com/2006/08 ··· inity%2F 에 나온 코드를 컴파일 되도록 수정 )

[code]
/* Short test program to test the pthread_setaffinity_np
* (which sets the affinity of threads to processors).
* Compile: gcc pthread_setaffinity_np_test.c
*            -o pthread_setaffinity_np_test -lm -lpthread
* Usage: ./pthread_setaffinity_test
*
* Open a "top"-window at the same time and see all the work
* being done on CPU 0 first and after a short wait on CPU 1.
* Repeat with different numbers to make sure, it is not a
* coincidence.
*/
#include <stdio.h>
#include <math.h>
#include <unistd.h>
#define __USE_GNU
#include <pthread.h>
double waste_time(long n)
{
    double res = 0;
    long i = 0;
    while (i <n * 200000) {
        i++;
        res += sqrt(i);
    }
    return res;
}
void *thread_func(void *param)
{
    unsigned long mask = 1; /* processor 0 */
    /* bind process to processor 0 */
    if (pthread_setaffinity_np(pthread_self(), sizeof(mask), (cpu_set_t *)&mask) <0) {
        perror("pthread_setaffinity_np");
    }
    /* waste some time so the work is visible with "top" */
    printf("result: %f\n", waste_time(2000));
    mask = 2;   /* process switches to processor 1 now */
    sleep(2);
    if (pthread_setaffinity_np(pthread_self(), sizeof(mask), (cpu_set_t *)&mask) <0) {
        perror("pthread_setaffinity_np");
    }
    /* waste some more time to see the processor switch */
    printf("result: %f\n", waste_time(2000));
    return 0;
}
int main(int argc, char *argv[])
{
    pthread_t my_thread;
    if (pthread_create(&my_thread, NULL, thread_func,
        NULL) != 0) {
        perror("pthread_create");
    }
    //pthread_exit(NULL);
    pthread_join(my_thread, NULL);
    return 0;
}

[/code]

---------------------------------

프로세스의 경우 소스는 다음과 같습니다.

[code]
/* Short test program to test sched_setaffinity
* (which sets the affinity of processes to processors).
* Compile: gcc sched_setaffinity_test.c
*            -o sched_setaffinity_test -lm
* Usage: ./sched_setaffinity_test
*
* Open a "top"-window at the same time and see all the work
* being done on CPU 0 first and after a short wait on CPU 1.
* Repeat with different numbers to make sure, it is not a
* coincidence.
*/
#include <stdio.h>
#include <math.h>
#include <unistd.h>
#define __USE_GNU
#include <sched.h>
double waste_time(long n)
{
    double res = 0;
    long i = 0;
    while(i <n * 200000) {
        i++;
        res += sqrt (i);
    }
    return res;
}
int main(int argc, char **argv)
{
    unsigned long mask = 1; /* processor 0 */
    /* bind process to processor 0 */
    if (sched_setaffinity(0, sizeof(mask), (cpu_set_t*)&mask) <0) {
        perror("sched_setaffinity");
    }
    /* waste some time so the work is visible with "top" */
    printf ("result: %f\n", waste_time (2000));
    mask = 2; /* process switches to processor 1 now */
    if (sched_setaffinity(0, sizeof(mask), (cpu_set_t*)&mask) <0) {
        perror("sched_setaffinity");
    }
    /* waste some more time to see the processor switch */
    printf ("result: %f\n", waste_time (2000));
    return 0;
}

[/code]

크리에이티브 커먼즈 라이센스
Creative Commons License

Function pointer

Posted at 2008/11/14 16:33 // in Programming // by Daniel

function pointer 사용법 간단 정리

> 선언
int형을 리턴하며 int형 인자 하나를 받는 함수를 포인터 형식으로 선언하는 경우
    int (*myhandler)( int );

> 할당
  int myfunction( int );
이런 형식의 함수가 있을 때
    myhandler = myfunction;
이렇게 하면 됨

> 실행
    result = myhandler(a);
  또는
    result = (*myhandler)(a);
로 쓴다.

> 인자로 넘길 때
    void pass_it( int (*myfp)(int) );
  이런식으로 씀.

참조 http://oopweb.com/cpp/documents/functio ··· fpt.html

크리에이티브 커먼즈 라이센스
Creative Commons License

역사상 최악의 소프트웨어 버그

Posted at 2008/09/26 14:24 // in Programming // by Daniel
http://www.wired.com/software/coolapps/ ··· %2F69355

2005년 기사입니다만,
소프트웨어 안정성이 얼마나 중요한지 다시 생각할 수 있는 내용입니다.
저는 원래 기계 신봉자라, (탱크 로켓 이런 걸 엄청 좋아했죠) 기계는 잘못할 수 가 없다는 믿음을 가졌었는데,
원자력 발전소 제어쪽 프로그램을 하다보니, 꼭 그런 건 아니구나 하는 깨달음이 있었습니다.
컴퓨터도 실수를 합니다. 그리고 그보다 수백배 더, 그걸 운용하는 사람과 그걸 만드는 프로그래머가 실수를 합니다.
요즘엔 프로그램이 정말 많이 사용됩니다. 몇년전부터 휴대폰의 소프트웨어 개발비가 하드웨어 개발비를 초과하기 시작한 걸로 알고 있고 전체 비용에서 비율이 기하급수적으로 늘고 있습니다.
그만큼 버그의 위험이, 그리고 그로 인한 재난의 발생 확률이 높아지는 거지요.
일부를 번역해봤습니다.

Last month automaker Toyota announced a recall of 160,000 of its Prius hybrid vehicles following reports of vehicle warning lights illuminating for no reason, and cars' gasoline engines stalling unexpectedly. But unlike the large-scale auto recalls of years past, the root of the Prius issue wasn't a hardware problem -- it was a programming error in the smart car's embedded code. The Prius had a software bug.

지난달(그러니까 2005년)에 16만대를 리콜한 도요타의 프리우스, 엔진이 갑자기 꺼지고 경고등이 이유없이 켜지는 등의 현상이었다. 이번 경우엔 소프트웨어 문제였다. 프리우스가 소프트웨어 버그가 있었던 것이다.

July 28, 1962 -- Mariner I space probe. A bug in the flight software for the Mariner 1 causes the rocket to divert from its intended path on launch. Mission control destroys the rocket over the Atlantic Ocean. The investigation into the accident discovers that a formula written on paper in pencil was improperly transcribed into computer code, causing the computer to miscalculate the rocket's trajectory.

화성탐사선 마리너 1호 얘깁니다. 4분만에 대서양으로 추락.

문제의 원인은 이렇다 저렇다 말이 많다고 하는데, 공통점은 소프트웨어 버그였으며 포트란 코드에서 한 글자를 잘못 써서, 궤도 계산이 지속적으로 발산하는 바람에 일어났고 엄청난 손실을 입었습니다.

1982 -- Soviet gas pipeline. Operatives working for the Central Intelligence Agency allegedly (.pdf) plant a bug in a Canadian computer system purchased to control the trans-Siberian gas pipeline. The Soviets had obtained the system as part of a wide-ranging effort to covertly purchase or steal sensitive U.S. technology. The CIA reportedly found out about the program and decided to make it backfire with equipment that would pass Soviet inspection and then fail once in operation. The resulting event is reportedly the largest non-nuclear explosion in the planet's history.

소련 가스 파이프라인 사고입니다. 이 파이프라인에 캐나다제 제어 시스템을 수입했는데 CIA에서 기술 유출로 단정하고 버그를 심었답니다. 핵폭발 이외의 가장 큰 폭발 사고.

1985-1987 -- Therac-25 medical accelerator. A radiation therapy device malfunctions and delivers lethal radiation doses at several medical facilities. Based upon a previous design, the Therac-25 was an "improved" therapy system that could deliver two different kinds of radiation: either a low-power electron beam (beta particles) or X-rays. The Therac-25's X-rays were generated by smashing high-power electrons into a metal target positioned between the electron gun and the patient. A second "improvement" was the replacement of the older Therac-20's electromechanical safety interlocks with software control, a decision made because software was perceived to be more reliable.

What engineers didn't know was that both the 20 and the 25 were built upon an operating system that had been kludged together by a programmer with no formal training. Because of a subtle bug called a "race condition," a quick-fingered typist could accidentally configure the Therac-25 so the electron beam would fire in high-power mode but with the metal X-ray target out of position. At least five patients die; others are seriously injured

Therac-25 엑스레이
이전의 20 버전에서 25로 업그레이드했는데 안전장치(safety interlock)를 소프트웨어로 교체함. 소프트웨어에 "race condition"(간단히 설명하면 어느쪽이 먼저 했냐에 따라 결과 값이 달라짐) 버그가 있는 걸 몰랐고 결과적으로 대량의 방사선을 누출하는 일이 생겨서 5명이 죽고 많은 사람이 다침.

1988 -- Buffer overflow in Berkeley Unix finger daemon. The first internet worm (the so-called Morris Worm) infects between 2,000 and 6,000 computers in less than a day by taking advantage of a buffer overflow. The specific code is a function in the standard input/output library routine called gets() designed to get a line of text over the network. Unfortunately, gets() has no provision to limit its input, and an overly large input allows the worm to take over any machine to which it can connect.

Programmers respond by attempting to stamp out the gets() function in working code, but they refuse to remove it from the C programming language's standard input/output library, where it remains to this day.

핑거 서비스 프로그램 버그. 그 유명한 버퍼 오버플로우 버그(문자열 버퍼등에 크기보다 더 많은 데이터를 보냄으로써 상대편 프로그램에 코드를 심을 수 있음)를 이용해 첫번째 인터넷 웜이 출현. 이 프로그램을 만든 모리스의 이름을 다서 모리스 웜이라고 불립니다. 당시 인터넷의 10%가 뚫려서 수천대의 서버가 다운되고 수십개 학교, 정부, 연구소가 공격당했습니다.
크리에이티브 커먼즈 라이센스
Creative Commons License

Makefile tip

Posted at 2008/09/09 14:48 // in Programming // by Daniel
$* <- 확장자가 없는 현재의 목표 파일(Target)

$@ <- 현재의 목표 파일(Target)

$< <- 현재의 목표 파일(Target)보다 더 최근에 갱신된 파일 이름

$? <- 현재의 목표 파일(Target)보다 더 최근에 갱신된 파일들 이름

 

 

main.o : main.c io.h
gcc -c $*.c

$* 는 확장자가 없는 현재의 목표 파일이므로 $* 는 결국 main 에 해당한다.

test : $(OBJS)
gcc -o $@ $*.c

$@는 현재의 목표 파일이다. 즉 test에 해당된다.

.c.o :
gcc -c $< (또는 gcc -c $*.c)
$< 는 현재의 목표 파일보다 더 최근에 갱신된 파일 이름이라고 하였다. .o 파일보다 더 최근에 갱신된 .c 파일은 자동적으로 컴파일이 된다. 가령 main.o를 만들고 난 다음에 main.c를 갱신하게 되면 main.c는 $<의 작용에 의해 새롭게 컴파일이 된다.

 

$<와 $?의 차이는 파일들을 다 얘기하느냐 아니냐이다

sdltest_speed: sdltest_speed.o md.o
    g++ $(LDFLAGS) -o $@ $<

일 때는

g++ `sdl-config --libs` -lSDL_image -o sdltest_speed sdltest_speed.o

가 실행되지만

sdltest_speed: sdltest_speed.o md.o
    g++ $(LDFLAGS) -o $@ $?

일 때는

g++ `sdl-config --libs` -lSDL_image -o sdltest_speed sdltest_speed.o md.o

가 실행된다.

참고 http://kldp.org/KoreanDoc/html/GNU-Make/GNU-Make-3.html#ss3.2

크리에이티브 커먼즈 라이센스
Creative Commons License

gcc 컴파일 옵션으로 커스텀 프로파일 해서 실행 트레이스 뽑기

Posted at 2008/09/04 10:36 // in Programming // by Daniel

실행 트레이스 뽑기

  • 이 링크 (원문 IBM) 에 보면 소스에 작은 글루 코드를 컴파일 시 붙여 콜 그래프를 뽑을 수 있다고 나옵니다.(gcc에서 지원하는 트레이스 뽑는 방법). –g 옵션과 -finstrument-functions 로 됨.

사용방법 :
1. Instrument.c
Instrument.c를 만들고 내용에
void main_constructor( void )
void main_deconstructor( void )
void __cyg_profile_func_enter( void *this, void *callsite )
void __cyg_profile_func_exit( void *this, void *callsite )

를 만들어 넣음. (trace.txt를 열고 닫고, 포인터 프린트하는 걸로 충분. 예제 코드 있음.)
#include <stdio.h>
#include <stdlib.h>
필요.
2. 컴파일
gcc –g -finstrument-functions test.c instrument.c –o test
로 컴파일

3. 실행
이러면 trace.txt가 나옴. 여기엔 들어가고 나온 함수의 주소가 나옴.

4. 심볼 이름 뽑고 정리하기
여기까지만 하고 addr2line으로 함수이름 뽑을 수 있고,
나머지 복잡한 것은 링크 참조. pvtrace라는 프로그램을 컴파일해서 콜 그래프 생성 예제가 있습니다.
Thread 정보 보고싶으면, self = pthread_self();으로 쓰레드 아이디 읽어와서 뿌리면 됨.

런타임에 함수 이름 뽑고 싶으면 몇가지 방법이 있을 듯 합니다.
libtrace_resolve (func, buf_func, CTRACE_BUF_LEN, NULL, 0); 등을 써서..

심볼 이름 찾는 또다른 방법은 여기 더 설명 나옴. backtrace, backtrace_symbols libbfd 등.
 이곳에서는 트레이스 함수를 좀더 많이 정보를 뿌리게 만들었음.


제가 실행해본 예입니다.

해당 방법으로 midori 브라우저(웹킷 기반의 가벼운 브라우저입니다) instrumentation

Makefile.in에서
 midori_OBJECTS = $(am_midori_OBJECTS) instrument.$(OBJEXT)
 이렇게 고치고
CFLAGS = @CFLAGS@ -finstrument-functions
이렇게 추가.
Makefile.am에서
midori_SOURCES = 에 instrument.c 추가 (이건 별 효과 없는 듯)
$ make clean
$ make
$ ./midori 
$ ~/misc/pvtrace/pvtrace midori 
$ dot -Tjpg graph.dot -o graph.jpg
그림 확인
$ eog graph.jpg

trace.txt를 얻어,

image

콜 그래프 생성했습니다.

크리에이티브 커먼즈 라이센스
Creative Commons License