|
|
By utahta, on 11月 26th, 2009%
MySQL 5.1.41 を GDB 使って解析するメモ。取っかかり編。
環境 : CentOS 5.2
ダウンロード
MySQL 5.1.41 のソースをダウンロードする。
インストール
/usr/local/mysql-5.1.41 へインストールする。
$ tar zxvf mysql-5.1.41.tar.gz
$ cd mysql-5.1.41
$ ./configure --prefix=/usr/local/mysql-5.1.41 --with-unix-socket-path=/usr/local/mysql-5.1.41/tmp/mysql.sock --with-charset=utf8 --with-extra-charsets=all --enable-thread-safe-client --with-plugins=innodb_plugin --with-readline --with-debug
$ make
$ make install
$ cd /usr/local/mysql-5.1.41
$ sudo cp share/mysql/my-medium.cnf /etc/my.cnf
GDB
最初に mysqld を gdb 経由で実行する。
$ gdb ./libexec/mysqld
GNU gdb Red Hat Linux (6.5-25.el5rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
立ち上がったら続けて、main に break ポイントを打ってみる。
(gdb) b main
Breakpoint 1 at 0x81b697a: file mysqld.cc, line 4261.
main は sql/mysqld.cc の 4261 行目にある。
とりあえず実行。
(gdb) r
Starting program: /usr/local/mysql-5.1.41/libexec/mysqld
[Thread debugging using libthread_db enabled]
[New Thread -1209051440 (LWP 10008)]
[Switching to Thread -1209051440 (LWP 10008)]
Breakpoint 1, main (argc=1, argv=0xbf96e304) at mysqld.cc:4261
4261 MY_INIT(argv[0]); // init my_sys library & pthreads
MY_INIT(argv[0]) のとこで止まった。(ちなみに MY_INIT() はマクロで、include/my_sys.h の 40 行目にある)
いま止まってるとこ付近のソースを表示する。
(gdb) l
4256 int win_main(int argc, char **argv)
4257 #else
4258 int main(int argc, char **argv)
4259 #endif
4260 {
4261 MY_INIT(argv[0]); // init my_sys library & pthreads
4262 /* nothing should come before this line ^^^ */
4263
4264 /* Set signal used to kill MySQL */
4265 #if defined(SIGUSR2)
main の直後 に MY_INIT() が呼ばれており、#else 〜 #endif で windows とそれ以外で main を切り分けてるのがなんとなく分かる。
ステップ実行して MY_INIT() に入る。
(gdb) s
my_init () at my_init.c:73
73 if (my_init_done)
Current language: auto; currently c
my_init() へ辿り着いた。my_init() は MY_INIT() の中で呼ばれており、mysys/my_init.c の 73 行目にある。
my_init_done の値を確認する。
(gdb) p my_init_done
$1 = 0 '\0'
0 が入ってる。my_init() 実行前だから当然っちゃ当然。(my_init_done は my_bool型(typedef char))
やっぱりステップ実行は便利ですね。
参考
GDB マニュアル
By utahta, on 11月 17th, 2009%
libevent のソースコードを参考にしつつ epoll を使ってみたメモ。
ただの興味本位。
動作は、Linux CentOS 5 で確認。
エラー処理とかはやってたりやってなかったり、わりと適当。
server.cpp
/**
* @file server.cpp
* @brief epoll test server.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/epoll.h>
#define LISTEN_PORT (12345) ///< 待ち受けポート
#define MAX_EVENTS (1000) ///< 最大イベント数
#define MAX_BACKLOG (10) ///< リクエスト待機キューの格納数
struct epollev
{
void (*callback)(int, void *); ///< callback function.
void *arg; ///< user data.
};
struct epollop
{
int epfd; ///< epoll fd.
struct epoll_event *events; ///< epoll events.
int num_events; ///< maximum number of events.
struct epollev *fds; ///< fds
};
static epollop g_epollop;
static void callback_accept( int fd, void *arg );
static void callback_client( int fd, void *arg );
//! @brief 強制終了
static void server_exit( const char *log )
{
perror( log );
exit( EXIT_FAILURE );
}
//! @brief イベント初期化
static void server_init()
{
int epfd, nfiles = MAX_EVENTS;
if( ( epfd = epoll_create( nfiles ) ) == -1 ){
server_exit( "epoll_create()" );
}
g_epollop.epfd = epfd;
g_epollop.events = new struct epoll_event[nfiles];
memset( g_epollop.events, 0, sizeof(struct epoll_event)*nfiles );
g_epollop.num_events = MAX_EVENTS;
g_epollop.fds = new epollev[nfiles];
memset( g_epollop.fds, 0, sizeof(struct epollev)*nfiles );
}
//! @brief イベント追加
static void server_add( int fd, int events, void (*callback)(int, void *), void *arg )
{
struct epoll_event epev = {0, {0}};
struct epollev *ev = &g_epollop.fds[fd];
int op = EPOLL_CTL_ADD;
if( ev->callback != NULL ){
op = EPOLL_CTL_MOD;
}
epev.data.fd = fd;
epev.events = events;
if( epoll_ctl( g_epollop.epfd, op, fd, &epev ) == -1 ){
perror( "epoll_ctl()" );
return;
}
ev->callback = callback;
ev->arg = arg;
}
//! @brief イベント削除
static void server_del( int fd )
{
struct epoll_event epev = {0, {0}};
struct epollev *ev = &g_epollop.fds[fd];
int op;
op = EPOLL_CTL_DEL;
epev.data.fd = fd;
memset( ev, 0, sizeof( struct epollev ) );
if( epoll_ctl( g_epollop.epfd, op, fd, &epev ) == -1 ){
perror( "epoll_ctl()" );
return;
}
close( fd );
}
//! @brief 受付
static void server_listen()
{
int sock;
struct sockaddr_in saddr;
if( ( sock = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ){
server_exit( "socket()" );
}
memset( &saddr, 0, sizeof( saddr ) );
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl( INADDR_ANY );
saddr.sin_port = htons( LISTEN_PORT );
if( bind( sock, (struct sockaddr *) &saddr, sizeof(saddr) ) != 0 ){
close( sock );
server_exit( "bind()" );
}
if( listen( sock, MAX_BACKLOG ) < 0 ){
close( sock );
server_exit( "listen()" );
}
server_add( sock, EPOLLIN, callback_accept, NULL );
}
//! @brief メインループ
static int server_loop( )
{
fprintf( stdout, "in server_loop.\n" );
while( true ){
int i, res;
int timeout = -1;
struct epoll_event *events = g_epollop.events;
res = epoll_wait( g_epollop.epfd, events, g_epollop.num_events, timeout );
if( res == -1 ){
server_exit( "epoll_wait()" );
}
for( i = 0; i < res; ++i ){
int fd = events[i].data.fd;
struct epollev *ev = &g_epollop.fds[fd];
ev->callback( fd, ev->arg );
}
}
return 0;
}
//! @brief 受付コールバック
static void callback_accept( int fd, void *arg )
{
fprintf( stdout, "in callback_accept. fd:%d\n", fd );
int sock;
struct sockaddr saddr;
socklen_t len = sizeof( struct sockaddr_in );
if( ( sock = accept( fd, (struct sockaddr *)&saddr, &len ) ) < 0 ){
perror( "accept()" );
return;
}
int flag = fcntl( sock, F_GETFL, 0 );
fcntl( sock, F_SETFL, flag | O_NONBLOCK );
fprintf( stdout, "accept. sock:%d\n", sock );
server_add( sock, EPOLLIN, callback_client, NULL );
}
//! @brief クライアントコールバック
static void callback_client( int fd, void *arg )
{
fprintf( stdout, "in callback_client. fd:%d\n", fd );
char buff[1024];
int ret = ::recv( fd, buff, sizeof(buff), 0 );
if( ret <= 0 ){
fprintf( stdout, "exit. fd:%d ret:%d\n", fd, ret );
server_del( fd );
}
else{
fprintf( stdout, "fd:%d -> %s\n", fd, buff );
}
}
int main()
{
server_init();
server_listen();
return server_loop();
}
client.cpp
/**
* @file client.cpp
* @brief test client.
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#define CONNECT_ADDR "127.0.0.1"
#define CONNECT_PORT (12345)
bool setnonblock(int fd)
{
int flags = fcntl( fd, F_GETFL );
if( flags < 0 ){
return false;
}
if( fcntl( fd, F_SETFL, flags |= O_NONBLOCK ) < 0 ){
return false;
}
return true;
}
int main(void)
{
int ret = 0, sock = 0, clilen = 0;
struct sockaddr_in cliaddr;
// socket
if( ( sock = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ){
puts( "socket error!!" );
return 1;
}
memset( &cliaddr, 0, sizeof( cliaddr ) );
cliaddr.sin_addr.s_addr = inet_addr( CONNECT_ADDR );
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons( CONNECT_PORT );
clilen = sizeof( cliaddr );
if( ( ret = connect( sock, (struct sockaddr *)&cliaddr, clilen ) ) < 0 ){
perror( "connect()" );
return 1;
}
if( !setnonblock( sock ) ){
perror( "setnonblock()" );
}
puts( "in client loop." );
char str[1024];
char *strpos;
while( 1 )
{
memset( str, 0, sizeof( str ) );
fgets( str, sizeof(str), stdin );
// \n を除去
if( ( strpos = strchr( str, '\n' ) ) != NULL ){
(*strpos) = '\0';
}
// exit
if( strncmp( str, "exit", 4 ) == 0 )
break;
// send
if( send( sock, str, strlen( str ) + 1, 0 ) < 0 ){
perror( "send()" );
break;
}
}
// 後始末
while( 1 ){
ret = recv( sock, str, sizeof(str), 0 );
if( ret <= 0 )
break;
}
close( sock );
return 0;
}
|
|