2008년 9월 26일 금요일

cmake 사용시 add_test 로 추가한 테스트 케이스에 대해서 valgrind(윈도라면 purify) 등으로 메모리 릭 검사를 하는 방법


ctest 의 기능중에 valgrind 로 테스트해주는 기능이 들어있네.
간단히 테스트 해보고 적어둔다. 대시보드 기능은 아직 안땡기는데..
꼴을 보니 곧 써봐야할것 같다.

어쨌건.. CMakeLists.txt 예제(일부분만)..
# CTest 를 인클루드하는것 잊지말자
include(CTest)
enable_testing()
# MEMORYCHECK_COMMAND 로 valgrind path 지정.
# 그외 기억해둘만한 변수로는
# MEMORYCHECK_COMMAND_OPTIONS
# MEMORYCHECK_SUPPRESSIONS_FILE
# 정도가 있다.
# 그냥 valgrind 를 쓰는 경우라면 딱히 지정안해도 되더라.
set(MEMORYCHECK_COMMAND valgrind)

# enable_testing, add_test 콤보는 지금까지 계속 써오던것.
# 이렇게 추가된 테스트케이스들을 대상으로 멤체크를 하려면
# ctest -T memcheck 명령을 주면 된다.
add_test(test_simple_decoding test_simple_decoding)
add_test(test_simple_encoding test_simple_encoding)


뭐 별거 없다. 내 개발환경이야 보통 리눅스일테니 valgrind 세팅은 생략해도 되고.. 그럼 남는건 기존 내가 즐겨 쓰던 세팅에 include(CTest) 가 추가된다는것 하나뿐이다.

위 테스트에 대해서 메모리 체크를 시행할때는 커맨드라인에서 ctest -T memcheck 를 때려준다. 모든 케이스에 대해서 메모리 체크가 이루어지고 마지막에 leak 이 있었던 케이스가 몇개인지 알려준다.

예를들어 내가 두개의 테스트를 가지고 있는데 모두 릭이 있을경우 요런 결과가 나온다.


그리고 릭을 잡고 났더니 요런 결과가 나온다. 결과가 눈에 잘 안들어 오는군.

추가.
make 에  NightlyMemCheck 란 타겟이 생겼는데 이게 위와 동일한듯. CTest 를 include 해서 그런지 못보던 타겟이 많이 보이는군....





2008년 9월 24일 수요일

linux 에서 특정 인터페이스 카드의 상태 읽어오기

MAC 어드레스 얻는것과 마찬가지로 걍 ioctl 쓰는건데.. 역시 샘플로 만들어둔 코드 그냥 지우기 뭐해서 적어둔다. 랜선이 껴진 상태인지 판단하기 위해서 만든 코드인데 현재 내 PC 네트웍을 죽일수가 없어서 아직 테스트는 안해봤다.

내일 출근하면 랜선 뺐다 껴보고, 인터페이스 올리고 내리고 등등 하면서 플래그값 변화를 체크해보자.



#include <sys/socket.h>
#include <sys/types.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void show_ifr_flags(unsigned int flags);

int main()
{
/* 인터페이스 이름을 eth0 으로 고정했는데 시스템 내의
* 인터페이스들을 얻어오려면 SIOCGIFCONF 참고
*/
const char* ifname = "eth0";

struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);

int s = socket(PF_INET, SOCK_STREAM, 0);
if(s<0) perror("socket failed"); /* TODO 더 나은 에러처리 */
int r = ioctl(s, SIOCGIFFLAGS, &ifr);
if(r<0) perror("ioctl failed"); /* TODO 더 나은 에러처리 */

show_ifr_flags(ifr.ifr_flags);

close(s);
}

/* 내 시스템에서 net/if.h 를 보면 이 flag 의 타입으로 unsigned short 가
* 쓰였다. 환경마다 다를것 같기도 헌데..
*/
void show_ifr_flags(unsigned int flags)
{
#define SHOW_FLAG(flag, desc) printf("%-16s = %d // %s\n", #flag, (flags&flag)!=0, desc);
SHOW_FLAG(IFF_UP, "Interface is up");
SHOW_FLAG(IFF_BROADCAST, "Broadcast address valid");
SHOW_FLAG(IFF_DEBUG, "Turn on debugging");
SHOW_FLAG(IFF_LOOPBACK, "Is a loopback net");
SHOW_FLAG(IFF_POINTOPOINT, "Interface is point-to-point link");
SHOW_FLAG(IFF_NOTRAILERS, "Avoid use of trailers");
SHOW_FLAG(IFF_RUNNING, "Resources allocated");
SHOW_FLAG(IFF_NOARP, "No address resolution protocol");
SHOW_FLAG(IFF_PROMISC, "Receive all packets");
SHOW_FLAG(IFF_ALLMULTI, "Receive all multicast packets");
SHOW_FLAG(IFF_MASTER, "Master of a load balancer");
SHOW_FLAG(IFF_SLAVE, "Slave of a load balancer");
SHOW_FLAG(IFF_MULTICAST, "Supports multicast");
SHOW_FLAG(IFF_PORTSEL, "Can set media type");
SHOW_FLAG(IFF_AUTOMEDIA, "Auto media select active");
SHOW_FLAG(IFF_DYNAMIC, "Dialup device with changing addresses");
#undef SHOW_FLAG
}


음.. 돌려보고 결과를 적어둔다.

각 상황별 결과 보기


정리하자면
랜선끼고 ifup
IFF_UP = 1 // Interface is up
IFF_RUNNING = 1 // Resources allocated

랜선끼고 ifdown
IFF_UP = 0 // Interface is up
IFF_RUNNING = 0 // Resources allocated

랜선뽑고 ifup
IFF_UP = 1 // Interface is up
IFF_RUNNING = 0 // Resources allocated

랜선뽑고 ifdown
IFF_UP = 0 // Interface is up
IFF_RUNNING = 0 // Resources allocated


linux 에서 특정 인터페이스 카드의 MAC 어드레스 읽어오기

갑자기 이런 코드가 필요해져서 짜봤다.
샘플수준의 코드



/*
eth0 인터페이스의 하드웨어 어드레스(mac) 을 찍어주는 코드

1. 인터페이스 이름은 eth0 라고 가정했다. 런타임에 알아내야 한다면
SIOCGIFCONF 등을 참고

2. 소켓하나가 필요. 다른 예제들을 보니 보통 DGRAM 으로
만드네.. STREAM 으로도 동작엔 문제가 없는듯 한데 DGRAM 이 쓰이는
이유가 뭘까

3. ifreq 구조체 만들어서 적절히 채우고

4. ioctl 콜

5. SIOCGIFHWADDR 를 요청했다면 결과는 ifreq 구조체의
ifr_hwaddr.sa_data 를 6 바이트 읽어내면 된다.
*/
#include <sys/socket.h>
#include <sys/types.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
const char* ifname = "eth0";

int s = socket(AF_INET, SOCK_DGRAM, 0);
if(s < 0) perror("socket fail"); /* TODO 에러처리 */

struct ifreq ifr;
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);

if(ioctl(s, SIOCGIFHWADDR, &ifr) < 0)
perror("ioctl fail"); /* TODO 에러처리 */

const unsigned char* mac = ifr.ifr_hwaddr.sa_data;
printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0],
mac[1],
mac[2],
mac[3],
mac[4],
mac[5]);

close(s);
}


음. 추가로 SIOCGIFADDR 로 ip 얻어오는 함수도 만들어봤다. ioctl 쪽은 정말 드럽다. sockaddr_in 때문에 헤더파일 몇개 추가해야 한다는것 잊지 말도록. man inet_ntoa 참고

int get_ip_address(char* ip, int iplen, const char* ifname)
{
int s;
struct ifreq ifr;
struct sockaddr_in* sin;

s = socket(AF_INET, SOCK_DGRAM, 0);
if(s<0) return -1;
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);

if(ioctl(s, SIOCGIFADDR, &ifr) < 0)
{
close(s);
return -1;
}

sin = (struct sockaddr_in*)&ifr.ifr_addr;
strncpy(ip,
inet_ntoa(sin->sin_addr),
iplen);

close(s);
return 0;
}




2008년 9월 22일 월요일

텍스트로 시퀀스 다이어그램 그리는 툴 Quick Sequence Diagram Editor



http://sdedit.sourceforge.net/

이런류의 툴이 필요해서 찾아봤는데 딱히 맘에 드는게 없네.
그나마 이놈이 내가 원하는 모양에 가장 가깝다.
자바로 만들어졌다는게 짱나지만..

아주 간단히 하나 그려보고 대강 적어둔다.
싱크메시지와 어싱크메시지를 섞어서 그려야 할일이 많은데
아직 방법을 모르겠군. 좀더 읽어보면 나오겠지.


아래 코드는 위 다이어그램을 생성한 소스.
UML 을 잘 모르니 용어등이 맞게 쓰였는지 모르겠군
뭐 대강 읽고 분위기만 알면 되는거지.

# 주석은 # 이다.
# 일단 제목을 지정하는 문법이 있는데 #![제목] 이렇게 써주면 된다.
# 아래 예를들어 제목을 달아봤다.
# 제목은 생략해도 되는데.. 생략하는게 오히려 보기엔 좋군
#![여기에 제목이 들어가야 하는데 별로 보기에 좋진 않어]

# 각 노드들을 여기서 정의하자.
# 사람모양을 그리려면 Actor 타입으로 해주면 되고 이놈은 모든 메시지가 어싱크형태로 된다.
# 만약 일반 노드에 어싱크 스타일의 메시징을 넣어주고 싶다면 속성에 [p] 를 추가하면 된다.
# 속성에 a 를 주면 name 이 생략되고 타입만 표시된다.
# 만약 어떤 노드가 동적으로 생성/파괴 된다면 /foo:server 식으로 앞에 / 를 붙여주면 된다.
# 이건 여기선 생략.
user:Actor
foo:foo[arp]
bar:bar[a]
baz:baz[a]

# 메시징
# foo 가 bar 에 hello 란 메시지를 보내려면 foo:bar.hello() 식으로
# 그외 문법이 좀더 복잡스럽던데 문서 참고
# 리턴값을 명시하는건 자주 쓰겠네. 여기 적어둔다.
# foo:result=bar.hello() 식으로 하면 된다.
user:foo.hello()
foo:bar.hello()
bar:fuck=baz.hello()
baz:대상을 지정 안하면 요렇게 나온다
bar:foo.damn 사실 꼭 메서드 형태로 적을 필요는 없다.
foo:user.damn()


# 요거이 노트
# 노트는 *숫자 오브젝트 로 시작해서 *숫자 로 끝내면 된다.
# 숫자가 뭘 의미하는지 모르겠네.
*1 bar
노트가 여기 들어간다
낄낄낄
*1

# 숫자는 요렇게 쓰는거구먼.
(2)user:foo.노트링크
*2 baz
그런데 노트 앞의 숫자는 뭔지 몰겠네
아 메시지앞에 (숫자) 를 붙여주면 링크가 되는구만
*2

음. 그외에
http://www.websequencediagrams.com/
라는 곳도 괜찮아 보이는데 웹서비스만 해주니 탈락.


2008년 9월 17일 수요일

cmake 로 빌드할때 다른툴(예를들어 lex 등)이 생성해낸 소스로 타겟을 만드는 방법..을 보여주는 간단하고 별 의미없는 예제

GENERATED 속성과 add_custom_target 을 이런식으로 써먹을수
있다..라는 글이지 lex 를 이렇게 쓰라는건 아니다. 만약 lex 를 써야
한다면 cmake 의 lex 지원 툴들을 먼저 찾아보고 없다면 macro 등으로
적절히 포장후 사용하자.


# 쪼까 드럽네.
# 나중에도 한번에 못만들어낼게 뻔하니 적어둔다.
# 샘플삼아 만들어본거라 이게 정석적인 방법이 맞는지는 모르겠고..
# 실무에 쓸상황이 오면 검색좀 해보고 쓰자.
#
#
# foo.yy.c 를 이용해서 foo 라는 실행파일을 생성
add_executable(foo foo.yy.c)
#
# 그런데 foo.yy.c 는 실제 소스가 아니고 cmake 시스템이 생성해내는
# 놈이므로 cmake 시스템에 그런 속성을 알려줘야 한다.
#
# 이건 사족이지만 짱나는게 있는데.. cmake 구버전 문서들은 cmake
# command 에 대문자를 주로 쓰지만 요즘엔 소문자도 쓰길래 나도 소문자로
# 써봤는데 커맨드이름이야 소문자가 잘 먹지만 인자중에 나오는 OUTPUT
# 이니 PROPERTIES 니 하는 것들은 대문자로 써줘야 돌아가는듯 하다.
set_source_files_properties(foo.yy.c PROPERTIES GENERATED true)
#
# add_custom_command 로 foo.l 로부터 foo.yy.c 를 생성하도록 해줬다.
# 몇가지 드러운점은.. 난 주로 소스디렉토리와 빌드디렉토리를 달리 하는데
# 그럴경우 디렉토리를 맟춰주는 삽질을 좀 해줘야 한다는것.
#
# 아래 코드를 보자면 빌드시에는 ${CMAKE_CURRENT_BINARY_DIR} 에서 작업이
# 진행되니 foo.yy.c 는 그자리( 이걸 소스디렉토리에 남길 이유는 없지 )
# 에 두고 flex 에 넘길 원본인 foo.l 을 지칭할때는 매번
# ${CMAKE_CURRENT_SOURCE_DIR} 를 지정해줬다.
add_custom_command(
OUTPUT foo.yy.c
COMMAND flex -o foo.yy.c ${CMAKE_CURRENT_SOURCE_DIR}/foo.l
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/foo.l)


보기 좋지 않군. 주석 제거한놈도 적어둬야지.

add_executable
(foo foo.yy.c)
set_source_files_properties(foo.yy.c PROPERTIES GENERATED true)
add_custom_command(
OUTPUT foo.yy.c
COMMAND flex -o foo.yy.c ${CMAKE_CURRENT_SOURCE_DIR}/foo.l
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/foo.l)






2008년 9월 11일 목요일

boost build 아주 간단한 유닛 테스트 방법

boost build 에 들어있는 testing.jam 을 읽어보고 알게된것들.
문서에는 testing 을 임포트하란말 따윈 없었던것 같은데.. 내가 못본건가
어쨌건 import testing 을 해야 하는걸 몰랐었는데 이제 알게됐다.

boost build 는 스크립트 덩어리들로.. bjam 이 이 스크립트들을 돌리는 인터프리터 엔진이다. 따라서 뭔가 막히면 *.jam 파일을 열어서 구경을 해보자.


조낸 간단한 프로그램 실행 테스트.
a.cpp 로 실행파일을 만들어서 실행여부만 확인하려면 요렇게 해보자.
import testing ;
run a.cpp ;
bjam 실행후엔 bin 아래쪽에서 위 실행파일의 출력등도 확인할수 있으니 잘 써먹자. 왕창 돌리고 통계를 내주는 툴도 있는데 아직 안써봤다.


테스트라면 valgrind 위에 올릴경우가 있을텐데 이럴때는 unit-test 룰을 쓰자.. run 룰에는 적용이 안되는 모양이다. 음.. 좀 그렇군?
import testing ;
unit-test test_a : a.cpp : <testing.launcher>valgrind ;
음 그런데 요렇게 돌릴경우 valgrind 의 출력(표준에러)은 그냥 콘솔로 나와버리네.
나중에 필요하면 더 삽질을 해보고.. 당장은 run 을 잘 써먹어야 겠다.









2008년 9월 8일 월요일

hunchentoot 한글(유니코드) 출력하기

전에도 고민했던 문제였는데 다시 만나게 되서 삽질을 또 해봤는데

이번에 한 삽질 보기


헐 시바 그런데 존내 간단한 해결방법이 있었네
http://osdir.com/ml/lisp.html-template.general/2007-05/msg00004.html
예전엔 왜 못봤지?


그래서 위 코드를 적용하고 나니 위의 foo 와 baz 가 이렇게 간단히 변했다.

(setf hunchentoot:*hunchentoot-default-external-format* :UTF-8)

(defun foo ()
"한글")
(defun baz ()
(let ((s (send-headers)))
(fill-and-print-template (create-template-printer "안녕 <!-- TMPL_VAR world -->")
'(:world "세상")
:stream s)))


아 쉬바...



2008년 9월 3일 수요일

rtmp client 몇가지 찾아봤으나 결국 실패.

위키에 적어두긴 뭐하니 그냥 여기 적는다.
디버깅 목적으로 rtmp client 가 필요하다고 해서 잠깐 찾아보고
겪은 일들.

  1. 가장 처음에 접근한것은 물론 gnash 그런데 잘 안되네 rtmpget 이랑 유틸리티가 있다는데 내가 받은 소스에는 Makefile 에서 주석처리가 되어있더라. 별 고민 안해보고 그냥 접었다.
  2. 좀 검색해보니 rtmpy 가 걸렸는데 client 쪽은 TODO 로 남아있더라. 아웃
  3. 결국 빌어먹을 자바로 만들어진 red5 를 가져다 해결. 문서와 내가 받은 소스가 버전이 달라 약간의 삽질이 있었다.

그런데 결국 red5 의 rtmp client 도 도움이 안됐다. 서버쪽에서 잘못주는게 분명했는데 그 에러를 못잡고 그냥 잘 받는것처럼 돌아가더군.

지금은 문제가 해결된 상황이라 하니 더 삽질 안하고 그냥 여기서 접고 메모만 남겨둔다.

common lisp 파일을 바이트 배열 꼴로 조금씩 끝까지 읽어주는 매크로.

lisp 은 역시 뭔가 좀 어렵군?
남이 만든 코드는 아주 이뻐 보이는데 내가 짜려고 하면 시궁창.
특히 매크로 만들거나 loop 를 쓰거나 하는 부분에선 손이 꼬이네?

어쩌다 보니 목적잃은 코드를 만들게 됐는데 그냥 버려두자니 좀 그래서 여기 적어둔다.
displaced array 를 배운건 큰 수확이었다.

;;;
;;; loop-with-file-contents
;;; buf : body 안에서 쓸, 파일의 일부가 담긴 배열
;;; path : 읽을 파일 경로
;;; element-type : 그냥 기본값쓰자. 박아두기 뭐해서 뽑아둔것.
;;; buffer-length : buf 크기. 한번에 얼마씩 읽을지 결정하는 값
;;; body : buf 에 담긴 내용을 알아서 써먹어라.
;;;
;;; 리턴값은 총 읽은 바이트 수
;;;
;;;
;;;
;;; 파일을 바이너리 형태로 읽어오고 싶어서 만들어봤다. 짜다 보니 아주
;;; 복잡해졌는데.. 남들은 이렇게 복잡하게 쓰지는 않을거란 생각이
;;; 드는군.
;;;
;;; 1. 어쩌다 보니 with- 라는 이름으로 했는데 짜놓고 보니 이게 안에서
;;; 루프를 도는 식이라 좀 쌩뚱맞은 이름이 되버렸네. 적절한 이름을
;;; 찾아보자... 였다가 그냥 성의없이 loop 를 앞에 붙여줬다 낄낄. with
;;; 로 시작해야 간지나는데 말이지.
;;;
;;; 2. 매크로의 body 부분에 buf 외에 buflen 까지 넘겨야 하는 상황을
;;; 피하기 위해서 buf 길이가 필요에 따라 줄어들길 바랬다. 즉 10 바이트
;;; 파일을 읽을때 버퍼길이가 4 라면 4,4,2 로 세번 바디가 실행이
;;; 되도록... 그런데 이짓을 하려고 보니 read-sequence 가 fill-pointer
;;; 를 받지를 않네. 좀더 고민을 해봐야 하나 하다가 문서에 displaced
;;; array 가 보이길래 그냥 그걸 썼다.
;;;
;;; 3. 매크로내에서 쓰이는 변수들이 매크로 외부의 심볼들과 쫑나지
;;; 않도록 gensyms 를 썼는데 이거 꽤나 성가시군.
;;;
;;;
(defmacro loop-with-file-contents ((buf path &key (element-type ''(unsigned-byte 8)) (buffer-length 8192)) &body body)
(let ((s (gensym))
(total-readed (gensym))
(readed (gensym)))
`(let ((,buf (make-array ,buffer-length :element-type ,element-type)))
(with-open-file (,s ,path :element-type ,element-type)
(loop
with ,total-readed fixnum = 0
for ,readed fixnum = (read-sequence ,buf ,s)
when (zerop ,readed) return ,total-readed
do (progn
(incf ,total-readed ,readed)
(let ((,buf (if (= ,readed ,buffer-length)
,buf
(make-array ,readed :element-type ,element-type :displaced-to ,buf))))
,@body)))))))


;;; 사용은 요렇게 하면 된다. 파일명이 od.lisp 인것은 원래 od 를 짜보려던것이라..
(loop-with-file-contents (buf "/tmp/od.lisp" :buffer-length 128)
(print buf))


추가.

;;; od 비슷하게 마저 짜봤다.
;;; format 의 ~X 에 인자를 줄때 두번째 인자가 캐릭터라고 되어있길래
;;; #\0 등을 주는 삽질을 했는데 그냥 '0 로 쿼트해서 주면 되는거였네...
;;; 너무 loop 에 의존하는 코딩을 하게 되는것 같아 찝찝하군.
(loop-with-file-contents (buf "/tmp/od.lisp" :buffer-length 16)
(loop for c fixnum across buf
do (format t " ~2,'0X" c)
finally (fresh-line)))


format 의 ~{ ~} 가 배열도 받았으면 정말 깔끔했을텐데 리스트만 받아먹더라.