コアダンプの数だけ強くなれるよ

見習いエンジニアの備忘log

C言語で簡易関数トレース

シンプルなprintf()デバッグコンパイルスイッチでON/OFFできるように実装する。

trace.c

#include <stdio.h>

#ifdef DEBUG_ENABLE
#define ENTRANCE(fmt, ...) \
    printf("%s:%u:===> IN  %s(" fmt ")\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__);
#else
#define ENTRANCE(fmt, ...)
#endif /* DEBUG_ENABLE */

#ifdef DEBUG_ENABLE
#define EXIT(fmt, ...) \
    printf("%s:%u:<=== OUT %s(" fmt ")\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__);
#else
#define  EXIT(fmt, ...)
#endif /* DEBUG_ENABLE */

#ifdef DEBUG_ENABLE
#define LOG_DEBUG(fmt, ...) \
    printf("%s:%u:%s():  " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__);
#else
#define LOG_DEBUG(fmt, ...)
#endif /* DEBUG_ENABLE */

int function(int a)
{
    int ret = 0;
    ENTRANCE("a=%d", a);

    EXIT("ret=%d", ret);
    return ret;
}

int main(void)
{
    ENTRANCE();

    LOG_DEBUG("hello world");

    int a = 1;
    int b = 2;
    LOG_DEBUG("a + b = %d", a+b);

    function(a);

    EXIT();
    return 0;
}



実行例

$ gcc -DDEBUG_ENABLE trace.c
$
$ ./a.out
trace.c:35:===> IN  main()
trace.c:37:main():  hello world
trace.c:41:main():  a + b = 3
trace.c:27:===> IN  function(a=1)
trace.c:29:<=== OUT function(ret=0)
trace.c:45:<=== OUT main()
$




あくまで簡易的なのでソースコードの規模が大きくなるほど見にくくなる予感。

trace.c:35:===> IN  main()
trace.c:27:  ===> IN  function(a=1)
trace.c:29:  <=== OUT function(ret=0)
trace.c:45:<=== OUT main()

こんな感じで、関数のIN/OUTでスタックが深くなるにしたがって表示が右にシフトしていくような機能が欲しい。
後は、タイムスタンプとsyslogのようにログレベルを指定してログの出力量を調整できる機能を追加したい。

コードの処理速度計測にgprofを試してみた

GNUのプロファイラ gprofでコードの処理速度計測を試してみた。
関数単位、LINE(行)単位での実行速度の計測ができる模様。

今回は関数単位でお試し。

使い方は"-pg"のオプションを付与してコンパイルしたバイナリを実行後にgprofコマンドをかませばOK。

gproftest.c

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

#define LOOP_BASE_COUNT (100*1000)

void function1(void)
{
    char buf1[1024] = {0};
    char buf2[1024] = {0};

    /* メモリ操作で負荷をかける */
    for(int i = 0; i < LOOP_BASE_COUNT; i++) {
        memset(buf1, 0xff, sizeof(buf1));
        memcpy(buf2, buf1, sizeof(buf2));
    }
}

void function2(void)
{
    char buf1[1024] = {0};
    char buf2[1024] = {0};

    for(int i = 0; i < LOOP_BASE_COUNT*10; i++) {
        memset(buf1, 0xff, sizeof(buf1));
        memcpy(buf2, buf1, sizeof(buf2));
    }
}

void function3(void)
{
    char buf1[1024] = {0};
    char buf2[1024] = {0};

    for(int i = 0; i < LOOP_BASE_COUNT*1000; i++) {
        memset(buf1, 0xff, sizeof(buf1));
        memcpy(buf2, buf1, sizeof(buf2));
    }
}

int main(void)
{
    function1();
    function2();
    function3();

    return 0;
}


Makefile

$ cat Makefile
gproftest: gproftest.c
        gcc -O2 -pg -std=gnu99 -o gproftest gproftest.c


実行例

$ make
gcc -O2 -pg -std=gnu99 -o gproftest gproftest.c
$
$ time ./gproftest

real    0m10.355s
user    0m10.341s
sys     0m0.004s
$
$
$ gprof gproftest gmon.out
Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total
 time   seconds   seconds    calls   s/call   s/call  name
 99.88     10.31    10.31        1    10.31    10.31  function3
  0.98     10.41     0.10        1     0.10     0.10  function2
  0.00     10.41     0.00        1     0.00     0.00  function1
...(後略)... 


[その他]
どうやらコンパイル時に -O3 で最適化すると上手くいかないぽい。

linuxでkbhit()

LinuxWindowsのkbhit()相当を実装する。

kbhit.h

#ifndef _KBHIT_H_
#define _KBHIT_H_

#include <stdbool.h>
#include <termios.h>
#include <unistd.h>

extern void KB_open(void);
extern void KB_close(void);
extern bool kbhit(void);
extern char linux_getch(void);


#endif /* _KBHIT_H_ */


kbhit.c

#include "kbhit.h"

static struct termios Old_set;
static struct termios New_set;
static int ReadChar = -1;

void KB_open()
{
    tcgetattr(0,&Old_set);
    New_set = Old_set;
    New_set.c_lflag &= ~ICANON;
    New_set.c_lflag &= ~ECHO;
    New_set.c_lflag &= ~ISIG;
    New_set.c_cc[VMIN] = 0;
    New_set.c_cc[VTIME] = 0;
    tcsetattr(0,TCSANOW,&Old_set);
}

void KB_close()
{
    tcsetattr(0,TCSANOW, &Old_set);
}

bool kbhit()
{
    char ch;
    int nread;

    if(ReadChar !=-1) {
        return true;
    }

    New_set.c_cc[VMIN]=0;
    tcsetattr(0,TCSANOW,&New_set);
    nread=read(0,&ch,1);
    New_set.c_cc[VMIN]=1;
    tcsetattr(0,TCSANOW,&New_set);

    if(nread == 1) {
        ReadChar = ch;
        return true;
    }

    return false;
}

char linux_getch()
{
    char ch;

    if(ReadChar != -1) {
       ch = ReadChar;
       ReadChar = -1;
       return (ch);
    }

    read(0,&ch,1);
    return(ch);
}

main.c

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

int main(void)
{
    KB_open();

    while(1) {
        if (kbhit()) {

            char ch = linux_getch();

            printf("kbhit: %c\n", ch);

            if ('q' == ch) {
                break;
            }
        }

        usleep(10000);
    }

    KB_close();
}


Makefile

kbloop: kbhit.o main.o
        gcc -o kbloop kbhit.o main.o

kbhit.o: kbhit.h kbhit.c
        gcc -c kbhit.c

main.o: main.c
        gcc -c main.c

clean:
        rm -f *.o


実行例

$ make
gcc -c kbhit.c
gcc -c main.c
gcc -o kbloop kbhit.o main.o
$
$ ./kbloop
kbhit: a
kbhit: b
kbhit: c
kbhit: d
kbhit: e
kbhit: f
kbhit: g
kbhit: q
$
$

HTML,JavaScriptで格子を描画

HTML5Canvas機能でオセロの盤面ぽい格子を描く。

canvas.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
</head>

<body>
  <!-- キャンバスの設定 -->
  <canvas id="canv" width="500" height="500"></canvas>
  <script>
    var ctx = document.getElementById("canv").getContext('2d');
    
    // 描画内容を指定する
    // 背景を500×500の緑で描画
    ctx.fillStyle = "rgb(0,128,0)";
    ctx.fillRect(0, 0, 500, 500);
    
    // 格子の設定
    ctx.strokeStyle = "white"
    ctx.lineWidth = 2;
    ctx.beginPath();
    
    // 縦線
    for (var v = 50; v < 500; v+=50) {
      ctx.moveTo(v, 0);
      ctx.lineTo(v, 500);
    }

    // 横線
    for (var h = 50; h < 500; h+=50) {
      ctx.moveTo(0, h);
      ctx.lineTo(500, h);
    }
    
    // 描画内容を実行する
    ctx.stroke();
  </script>

</body>

</html>


実行例
f:id:segmentation-fault:20170208142347p:plain

C言語でbuffer overrun

お呼びでない関数を実行する。

overrun.c

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

void goast(void)
{
    printf("Boo!\n");
}

void dummy(void)
{
    char a[1] = {0};

    a[9] = &goast;
}

int main(void)
{
    dummy();
    return 0;
}


実行例

$ gcc overrun.c
overrun.c: In function 'dummy':
overrun.c:13: warning: assignment makes integer from pointer without a cast
$ ./a.out
Boo!
Segmentation fault

HTML,JavaScriptでブラウザに時刻表示

HTML, JavaScriptの最初の一歩と言えばコレ。

[仕様]

  • 左上に現在時刻を"HH:MM:SS"形式で表示。'ex) 19:02:34'

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
    <div id="disp" style="font-size:48px;text-align:left;"></div>
    <script>
        setInterval(showTime, 500);
        function showTime() {
            var tv = new Date();
            $("disp").innerHTML  = 
                ("0" + tv.getHours()).slice(-2) + ":" + 
                ("0" + tv.getMinutes()).slice(-2) + ":" + 
                ("0" + tv.getSeconds()).slice(-2);
        }

        function $(id) { return document.getElementById(id); }
    </script>
</body>
</html>

gnuplotで正規分布を表示

gnuplot正規分布のグラフをterminal上に表示する。環境はLinux(CentOS 6)

[手順]

  1. perl正規分布に従う乱数のリストをファイルに出力
  2. gnuplotでterminalにグラフ出力


事前準備(perl, gnulpotのinstall)

$ sudo yum -y install gnuplot
$ sudo yum -y install perl


正規分布に従う乱数を出力するスクリプト
ndist.pl

#!/usr/bin/perl

$Pi = 3.14159265359;

$input = 1.0;
$sigma = 0.01;
$count = 10000;

while ($count > 0) {
    printf("%lf\n", RandBoxMuller($input, $sigma));
    $count--;
}

sub RandBoxMuller {
    my ($m, $sigma) = @_;
    my ($r1, $r2) = (rand(), rand());

    while ($r1 == 0) { $r1 = rand(); }

    return ($sigma * sqrt(-2 * log($r1)) * sin(2 * $Pi * $r2)) + $m;
}


gnuplotスクリプト
plothist.gp

reset

# set parameters
interval=100
min=0.950
max=1.050
width=(max-min)/interval

#function used to map a value to the intervals
hist(x,width)=width*floor(x/width)+width/2.0
set terminal dumb
set xrange [min:max]
set yrange [0:500]
set xtics min,(max-min)/5,max
set boxwidth width*0.9
set style fill solid 0.5 #fillstyle
set tics out nomirror
set xlabel "x"
set ylabel "Frequency"

#calculate and plot
plot "test.dat" u (hist($1,width)):(1.0) smooth freq w boxes


実行用シェルスクリプト
show_histgram.sh

#!/bin/sh

/usr/bin/perl ./ndist.pl > test.dat
/usr/bin/gnuplot plothist.gp


実行結果

$ ./show_histgram.sh



  Frequency
    500 ++------------------------------------------------------------------+
         |                       "test.dat" u (hist($1,width)):(1.0) ****** |
         |                                                                  |
         |                               ***                                |
    400 ++                               *****                              |
         |                             ********                             |
         |                            *********                             |
    300 ++                           ************                           |
         |                          *************                           |
         |                          ***************                         |
         |                         ****************                         |
    200 ++                        ******************                        |
         |                       *********************                      |
         |                      ***********************                     |
    100 ++                     ************************                     |
         |                    ***************************                   |
         |                  *******************************                 |
         |               ************************************               |
      0 ++--------*-************************************************-*------+
         +            +             +            +             +            +
        0.95         0.97          0.99         1.01          1.03         1.05
                                          x
$