c++2013.07.03 21:37

누가 이 함수를 호출하였을까?


개발환경 : C++/STL/Linux/gcc

디버깅을 하다보면, 대체 어디서 이 함수를 호출했을까 궁금해질때가 있다.  이럴때 호출한 함수들을 역순으로 알아낼 수 있는 함수 루틴. 

#include <execinfo.h>

void call_stack_dump() {

void *   array[50];

char **  messages;

int      size, i;


size     = backtrace(array, 50);

messages = backtrace_symbols(array, size);


// skip first stack frame (points here)

for (i = 0; i < size; ++i){

fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]);

}


free(messages);

}


컴파일 할때에 -g -rdynamic 옵션을 주어야 한다. 이 방법 말고도 프로그램을 바로 죽여도 좋다면 더 쉬운 방법이 있다.


abort()를 이용하여 코어덤프하고 종료시키자

 #include <stdlib.h>

 void abort(void);


이렇게 하면 core 파일이 생기는데,  gdb를 이용하여 back trace하면 자세히 확인할 수 있다. 



core 파일을 이용하여 GDB로 Backtrace 하기


$ gdb chat4 core.2838

Core was generated by `/work/chat4 -D'.

Program terminated with signal 6, Aborted.

#0  0xffffe410 in __kernel_vsyscall ()

(gdb) bt

#0  0xffffe410 in __kernel_vsyscall ()

#1  0x00943df0 in raise () from /lib/libc.so.6

#2  0x00945701 in abort () from /lib/libc.so.6

#3  0x080579ed in SigAlarm (signo=14) at chat4.cc:3799

#4  <signal handler called>

#5  0x00982bbb in _int_free () from /lib/libc.so.6

#6  0x00983329 in free () from /lib/libc.so.6

#7  0xf7f50fe1 in operator delete(void*) () from /usr/lib/libstdc++.so.6

#8  0xf7f2dbad in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep::_M_destroy(std::allocator<char> const&) () from /usr/lib/libstdc++.so.6

#9  0xf7f30757 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() () from /usr/lib/libstdc++.so.6

#10 0x08065515 in __gnu_cxx::new_allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::destroy (this=0xfff3c417, __p=0x9e23750)

...


불특정한 순간에 무한루프에 빠지는 증상을 찾기 위해, SIGALRM을 설정하여, 알람이 발생한 경우 abort()를 호출하여 core dump 하도록 하였다. 이 경우는 string 을 free 하다가 미궁(?)에 빠진 것인데... 아직 정확한 원인은 못 찾고 있다. ㅠㅠ


Posted by Jadumate
sqlite32013.06.11 16:57

그냥 보통 방법으로 INSERT와 DELETE등의 쿼리를 반복적으로 수행할때에 sqlite3의 성능은 형편없이 떨어지는데, 이럴때에는 Transaction을 이용해야 한다. 100개의 INSERT에 5초이상 걸리기도 하니 Transaction은 필수적이다.


int TSqlite::ExecuteMulti(const list<string> &queries)

{

// begin

sqlite3_exec(this->m_db, "BEGIN TRANSACTION;", 0, 0, 0);


list<string>::const_iterator ils = queries.begin();

while(ils != queries.end()){

sqlite3_exec(this->m_db, ils->c_str(), 0, 0, 0);

ils++;

}


// end

sqlite3_exec(this->m_db, "END TRANSACTION;", 0, 0, 0);


return sqlite3_total_changes(this->m_db);

}


sqlite3 stmt를 사용하지 않고, 간단히 구성해 보았다.  BEGIN - query - END 형식으로, 최종적으로 Effected Rows를 돌려주도록 하였다. 

테스트로 INSERT 600개를 연속으로 보냈을때에, 실행시간은 약 5초-> 0.05초로 백배 이상 빨라졌다. 

Posted by Jadumate
sqlite32013.05.24 20:55

sqlite3에 대한 C++ wrapper class 를 만들어 보았다. 


간단하게 INSERT와 SELECT 정도만 편하게 했으면 좋겠다 싶은데, sqlite라는게 그리 간단하지가 않다. (gdbm 정도의 편리함을 기대했다면 오산이다). db를 open하고 close하는 사이에 prepare하고 free, finalize 하는게 생각보다 까다롭다. 


그래서 요것만 후다닥 구현.


class TSqlite

{

protected :

bool m_isError;

sqlite3 *m_db;


private :

bool Send(const char *strQuery);


public:

TSqlite(const char *strFileName)

{

m_isError = (sqlite3_open(strFileName, &m_db) == SQLITE_OK) ? false : true;

}


~TSqlite()

{

if(m_db) sqlite3_close(m_db);

}


bool isError()

{

return m_isError;

}


int Execute(const char *strQuery);

int Select (const char *strQuery, list< vector<string> > &rResult);

};

소스1 : sqlite.h


bool TSqlite::Send(const char *strQuery)

{

    sqlite3_stmt *stmt;

    bool result = true;


    int rc = sqlite3_prepare(this->m_db, strQuery, strlen(strQuery), &stmt, NULL);

    if(rc != SQLITE_OK) return false;


    if(sqlite3_step(stmt) != SQLITE_DONE) {

        fprintf(stderr, "line %d: %s (%s)\n", __LINE__, sqlite3_errmsg(this->m_db), strQeury);

        result = false;

    }

    sqlite3_reset(stmt);

    sqlite3_finalize(stmt);


    return result;

};


int TSqlite::Select(const char *strQuery, list< vector<string> >  &rResult)

{

int  nColumn = 0;

char tbuf[64];

sqlite3_stmt *stmt;

vector<string> row1;


sqlite3_prepare(this->m_db, strQuery, -1, &stmt, NULL);


while(sqlite3_step(stmt) == SQLITE_ROW)

{

nColumn = sqlite3_column_count(stmt);


row1.clear();

for(int i = 0; i < nColumn ; i++) {

switch(sqlite3_column_type(stmt, i)){

case SQLITE_TEXT:

row1.push_back((const char *)sqlite3_column_text(stmt, i));

break;

case SQLITE_INTEGER:

sprintf(tbuf, "%d:", sqlite3_column_int(stmt, i));

row1.push_back(tbuf);

break;

case SQLITE_FLOAT:

sprintf(tbuf, "%f:", sqlite3_column_double(stmt, i));

row1.push_back(tbuf);

break;

case SQLITE_NULL:

break;

default:

break;

}

}

rResult.push_back(row1);

}

sqlite3_reset(stmt);

sqlite3_finalize(stmt);


return rResult.size();

}


int TSqlite::Execute(const char *strQuery)

{

sqlite3_stmt *stmt;

int result = 0;


sqlite3_prepare(this->m_db, strQuery, -1, &stmt, NULL);


// begin

this->Send("BEGIN;");

if(sqlite3_step(stmt) != SQLITE_DONE){

fprintf(stderr, "line %d: %s  (%s)\n", __LINE__, sqlite3_errmsg(this->m_db), strQuery);

result = -1;

}

// commit

this->Send("COMMIT;");


result = sqlite3_total_changes(this->m_db);


sqlite3_reset(stmt);

sqlite3_finalize(stmt);


return result;

}

소스2 sqlite.cc



이렇게 클래스를 만들어 두고 나니, 아래처럼 아주 간단하게 사용할 수 있다. 

TSqlite sql(FILE_MY_DB);

if(sql.isError()) return -1;


// 결과를 받아올 리스트 생성

list< vector<string> > rows;


// 쿼리만들기 & 결과 가져오기

sprintf(strQuery, "SELECT * from table WHERE id = '%s';", strId);

int nRows = sql.Select(strQuery, rows);


// 루프돌면서 결과값 출력

list< vector<string> >::iterator it = rows.begin();

while(it != rows.end()){

      // 레코드 1개 얻어오기

vector<string> &rRow = *it;


// 컬럼별로 출력

for(int i=0; i< rRow.size(); i++)

    cout << rRow[i] << endl;


it++;

}







Posted by Jadumate