Valgrind – dynamic code analysis tool – part III – Helgrind

Valgrind – Helgrind – concurrency issue detection.

# Helgrind
http://valgrind.org/docs/manual/hg-manual.html
--tool=helgrind
[root@localhost valgrindTest]# cat simpleDataRace.c
/* Code from Valgrind Manual */
#include <pthread.h>

int var = 0;

void* child_fn ( void* arg ) {
 var++; /* Unprotected relative to parent */ /* this is line 6 */
 return NULL;
}

int main ( void ) {
 pthread_t child;
 pthread_create(&child, NULL, child_fn, NULL);
 var++; /* Unprotected relative to child */ /* this is line 13 */
 pthread_join(child, NULL);
 return 0;
}
[root@localhost valgrindTest]# gcc -Wall -g -lpthread -o simpleDataRace simpleDataRace.c
[root@localhost valgrindTest]# valgrind --tool=helgrind ./simpleDataRace
==5733== Helgrind, a thread error detector
==5733== Copyright (C) 2007-2011, and GNU GPL'd, by OpenWorks LLP et al.
==5733== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==5733== Command: ./simpleDataRace
==5733==
==5733== ---Thread-Announcement------------------------------------------
==5733==
==5733== Thread #1 is the program's root thread
==5733==
==5733== ---Thread-Announcement------------------------------------------
==5733==
==5733== Thread #2 was created
==5733==    at 0x6D8098: clone (in /lib/libc-2.5.so)
==5733==    by 0x400B468: pthread_create_WRK (hg_intercepts.c:255)
==5733==    by 0x400B53D: pthread_create@* (hg_intercepts.c:286)
==5733==    by 0x804849E: main (simpleDataRace.c:12)
==5733==
==5733== ----------------------------------------------------------------
==5733==
==5733== Possible data race during read of size 4 at 0x8049724 by thread #1
==5733== Locks held: none
==5733== at 0x804849F: main (simpleDataRace.c:13)
==5733==
==5733== This conflicts with a previous write of size 4 by thread #2
==5733== Locks held: none
==5733== at 0x804845F: child_fn (simpleDataRace.c:6)
==5733== by 0x400B5B8: mythread_wrapper (hg_intercepts.c:219)
==5733== by 0x798831: start_thread (in /lib/libpthread-2.5.so)
==5733== by 0x6D80AD: clone (in /lib/libc-2.5.so)
==5733==
==5733== ----------------------------------------------------------------
==5733==
==5733== Possible data race during write of size 4 at 0x8049724 by thread #1
==5733== Locks held: none
==5733== at 0x80484A7: main (simpleDataRace.c:13)
==5733==
==5733== This conflicts with a previous write of size 4 by thread #2
==5733== Locks held: none
==5733== at 0x804845F: child_fn (simpleDataRace.c:6)
==5733== by 0x400B5B8: mythread_wrapper (hg_intercepts.c:219)
==5733== by 0x798831: start_thread (in /lib/libpthread-2.5.so)
==5733== by 0x6D80AD: clone (in /lib/libc-2.5.so)
==5733==
==5733==
==5733== For counts of detected and suppressed errors, rerun with: -v
==5733== Use --history-level=approx or =none to gain increased speed, at
==5733== the cost of reduced accuracy of conflicting-access information
==5733== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
[root@localhost valgrindTest]#
Posted in Static Code Analysis | Tagged , , | 1 Comment

Valgrind – dynamic code analysis tool – part II – fd leak

Valgrind – File descriptor (fd) leak detection trial

# File descriptor (fd) leak detection
--track-fds=yes
[root@localhost valgrindTest]# cat fdLeakTry.c
/* daveti's stupid code for fd leak */
#include <stdio.h>
int main( int argc, char *argv[])
{
 FILE *fdPtr = fopen("daveti.log", "rb");
 if ( argc != 1)
 {
 printf("FD leaking!n");
 return 1;
 }
 fclose(fdPtr);
 return 0;
}
[root@localhost valgrindTest]# gcc -Wall -g -o fdLeakTry fdLeakTry.c
[root@localhost valgrindTest]# valgrind --track-fds=yes --log-file=./fdLeakTry.log ./fdLeakTry hello
FD leaking!
[root@localhost valgrindTest]# cat fdLeakTry.log
==17725== Memcheck, a memory error detector
==17725== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==17725== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==17725== Command: ./fdLeakTry hello
==17725== Parent PID: 4801
==17725==
==17725==
==17725== FILE DESCRIPTORS: 5 open at exit.
==17725== Open file descriptor 4: daveti.log
==17725== at 0x6C85A3: __open_nocancel (in /lib/libc-2.5.so)
==17725== by 0x669AA4: _IO_file_fopen@@GLIBC_2.1 (in /lib/libc-2.5.so)
==17725== by 0x65E75C: __fopen_internal (in /lib/libc-2.5.so)
==17725== by 0x65E7BB: fopen@@GLIBC_2.1 (in /lib/libc-2.5.so)
==17725== by 0x804843B: main (fdLeakTry.c:5)
==17725==
==17725== Open file descriptor 3: /home/daveti/valgrindTest/fdLeakTry.log
==17725==    <inherited from parent>
==17725==
==17725== Open file descriptor 2: /dev/pts/1
==17725==    <inherited from parent>
==17725==
==17725== Open file descriptor 1: /dev/pts/1
==17725==    <inherited from parent>
==17725==
==17725== Open file descriptor 0: /dev/pts/1
==17725==    <inherited from parent>
==17725==
==17725==
==17725== HEAP SUMMARY:
==17725==     in use at exit: 352 bytes in 1 blocks
==17725==   total heap usage: 1 allocs, 0 frees, 352 bytes allocated
==17725==
==17725== LEAK SUMMARY:
==17725==    definitely lost: 0 bytes in 0 blocks
==17725==    indirectly lost: 0 bytes in 0 blocks
==17725==      possibly lost: 0 bytes in 0 blocks
==17725==    still reachable: 352 bytes in 1 blocks
==17725==         suppressed: 0 bytes in 0 blocks
==17725== Rerun with --leak-check=full to see details of leaked memory
==17725==
==17725== For counts of detected and suppressed errors, rerun with: -v
==17725== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 12 from 8)
[root@localhost valgrindTest]#
Posted in Static Code Analysis | Tagged , , | Leave a comment

Valgrind – dynamic code analysis tool – part I – basic trial and hints

We are recently trying to find a tool providing the ability for concurrency issue detection. For this topic, generally, I believe in 3 steps: 1. CPR (capacity, performance, redundancy) testing, 2. Static code analysis, 3. Dynamic code analysis. CPR testing is the same thing for each product before delivery; Coverity/Klocwork (not free!) would be the ones for SCA; Insure++/Bulleye could be the ones standing for DCA. However, for DCA requesting concurrency specific and free, Valgrind would be our first choice. From this post, we will try to go thru some details about Valgrind and then focus on some key detection like concurrency. May it help.

# Download source code
http://valgrind.org/downloads/current.html

# Unzip the pkg
bunzip valgrind-3.7.0.tar.bz2
tar -xvf valgrind-3.7.0.tar

# Build, install and test
./configure
make
make install
valgrind ls -l
[root@localhost valgrindTest]# ldd /usr/local/bin/valgrind
        linux-gate.so.1 =>  (0x00e3b000)
        libc.so.6 => /lib/libc.so.6 (0x00606000)
        /lib/ld-linux.so.2 (0x005e7000)
[root@localhost valgrindTest]#

# Or rpm installation for Linux
http://rpmfind.net/linux/rpm2html/search.php?query=valgrind

# GUIs
http://valgrind.org/downloads/guis.html
# Check and find the the useful tools
http://valgrind.org/docs/manual/manual-intro.html
1. Memcheck is a memory error detector. It helps you make your programs, particularly those written in C and C++, more correct.
2. Cachegrind is a cache and branch-prediction profiler. It helps you make your programs run faster.
3. Callgrind is a call-graph generating cache profiler. It has some overlap with Cachegrind, but also gathers some information that Cachegrind does not.
4. Helgrind is a thread error detector. It helps you make your multi-threaded programs more correct.
5. DRD is also a thread error detector. It is similar to Helgrind but uses different analysis techniques and so may find different problems.
6. Massif is a heap profiler. It helps you make your programs use less memory.
7. DHAT is a different kind of heap profiler. It helps you understand issues of block lifetimes, block utilisation, and layout inefficiencies.
8. SGcheck is an experimental tool that can detect overruns of stack and global arrays. Its functionality is complementary to that of Memcheck: SGcheck fi
nds problems that Memcheck can't, and vice versa..
9. BBV is an experimental SimPoint basic block vector generator. It is useful to people doing computer architecture research and development.
10. Lackey is an example tool that illustrates some instrumentation basics.
11. Nulgrind is the minimal Valgrind tool that does no analysis or instrumentation, and is only useful for testing purposes.
# Quick start using Memcheck
http://valgrind.org/docs/manual/quick-start.html
[root@localhost valgrindTest]# cat memcheckTry.c
/* Code from Valgrind Manual */
#include <stdlib.h>

 void f(void)
 {
 int* x = malloc(10 * sizeof(int));
 x[10] = 0; // problem 1: heap block overrun
 } // problem 2: memory leak -- x not freed

 int main(void)
 {
 f();
 return 0;
}
[root@localhost valgrindTest]#
gcc -Wall -g -o memcheckTry memcheckTry.c
[root@localhost valgrindTest]# valgrind --leak-check=yes ./memcheckTry
==16907== Memcheck, a memory error detector
==16907== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==16907== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==16907== Command: ./memcheckTry
==16907==
==16907== Invalid write of size 4
==16907==    at 0x80483BF: f (memcheckTry.c:6)
==16907==    by 0x80483DC: main (memcheckTry.c:11)
==16907==  Address 0x4017050 is 0 bytes after a block of size 40 alloc'd
==16907==    at 0x400705E: malloc (vg_replace_malloc.c:263)
==16907==    by 0x80483B5: f (memcheckTry.c:5)
==16907==    by 0x80483DC: main (memcheckTry.c:11)
==16907==
==16907==
==16907== HEAP SUMMARY:
==16907==     in use at exit: 40 bytes in 1 blocks
==16907==   total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==16907==
==16907== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==16907==    at 0x400705E: malloc (vg_replace_malloc.c:263)
==16907==    by 0x80483B5: f (memcheckTry.c:5)
==16907==    by 0x80483DC: main (memcheckTry.c:11)
==16907==
==16907== LEAK SUMMARY:
==16907==    definitely lost: 40 bytes in 1 blocks
==16907==    indirectly lost: 0 bytes in 0 blocks
==16907==      possibly lost: 0 bytes in 0 blocks
==16907==    still reachable: 0 bytes in 0 blocks
==16907==         suppressed: 0 bytes in 0 blocks
==16907==
==16907== For counts of detected and suppressed errors, rerun with: -v
==16907== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 12 from 8)
[root@localhost valgrindTest]#
# General compiling options
-g
-fno-inline (C++)
-Wall
NOTE: Turn off '-Ox' or only go with '-O'

# Logging options
--log-file=<filename>

# XML report options (for GUI's consuming)
--xml=yes --xml-file=<filename>

# Memory leak detection
--tool=memcheck (default)

Posted in Static Code Analysis | Tagged , , , | 1 Comment

bind() error 0x63 – Cannot assign requested address

Recently encountered a bind() error 0x63 – cannot assign requested address. After some effort on debugging, I list 2 possibilities here for reference when handling such this error code from bind().  Generally, if it is related with bad socket close/cleanup, we have to fix the bug within application code or wait some time to make sure the scoket is shutdown by OS finally; if it is related with IP removed, we need to add this IP again. May it help.

1. socket state legacy - netstat
<sch01-s00c06h0:root>/root:
# netstat -an | grep 10.206.254.1
tcp        0      0 10.206.254.1:3868           10.206.218.4:33594          TIME_WAIT

2. IP removed on certain interface - ifconfig
<sch01-s00c06h0:root>/root:
# ifconfig -a | grep 10.206.254.1
<sch01-s00c06h0:root>/root:
# ifconfig eth0.800:wAAAA 10.206.254.1 netmask 255.255.128.0
<sch01-s00c06h0:root>/root:
# ifconfig -a | grep 10.206.254.1
          inet addr:10.206.254.1  Bcast:10.206.255.255  Mask:255.255.128.0

PS. extra info of IP before removed - ifconfig
eth0.800:wAAAA Link encap:Ethernet  HWaddr 52:54:00:50:00:60
          inet addr:10.206.254.1  Bcast:10.206.255.255  Mask:255.255.128.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
Posted in Programming | Tagged , , , | Leave a comment

gprof and dot – some hints using gprof, gprof2dot and dot

Recently done a performance tuning using gprof, gprof2dot and dot. While there are already a lot of webs talking about them respectively or generally. This post is trying to provide some hints from my real experience, as well as some usefully links for reference. May it help.

(provided by Ethan.Yu@alcatel-lucent.com)

Hint 1: ‘-pg’ is needed both in compiling and linking: If the binary is generated from source within one cmd, please no worry about this. However, as long as ‘Make’ is used. Make sure you have ‘-pg’ in the 2 stages. Otherwise, call graph data would be lost in the gprof report.

Hint 2: gprof: gmon.out file is missing call-graph data: There are 2 possibilities worth checking. A, there is no call graph indeed as our code is simple; B, ‘-pg’ is only included during the linking stage but not compiling.

Hint 3: which kind of gprof report is in favor of gprof2dot: ‘gprof BIN gmon.out > report.txt’ is fairly good enough.

Hint 4: commands summary:

gcc -g -pg -o BIN SRC.c
———————————
gcc -g -pg -c SRC.c
gcc -pg -o BIN SRC.o
———————————
gprof BIN gmon.out > report.txt
———————————
gprof2dot report.txt > report.dot
———————————
dot -Tpng -oreport.png report.dot

Hint 5: useful links:

gprof and gprof2dot brief intro (Chinese version)

‘-pg’ option for gprof

gprof2dot

dot

Posted in IDE_Make, Programming, Stuff about Compiler | Tagged , , | Leave a comment

A bad include in C – why static code analysis is needed for modern C/C++ projects

We have already had a long discussion and argument for ‘include’ mechanism in C/C++, no matter good or bad, and this is not my intention here. I am writing this post to show a confusing compiler error we have recently encountered and debugged for some time and trying to make my point – why static code analysis (SCA) is important for modern C/C++ projects. I am not saying compiler’s not good but saying SCA+compiler making the world better!

/home/daveti/Ctest/cCompiler: cat badHeader.h
// A bad header missing ';'
// badHeader.h
typedef struct _boringStruct
{
 int a;
 int b;
 int c;
} BoringStruct

/home/daveti/Ctest/cCompiler: cat useBadHeader.c
#include <stdio.h>
#include "badHeader.h"

int main()
{
 BoringStruct bs;
 bs.a = 1;
 bs.b = 2;
 bs.c = 3;
 printf( "a=%d, b=%d, c=%dn", bs.a, bs.b, bs.c);
 return 0;
}

/home/daveti/Ctest/cCompiler: gcc -o useBadHeader useBadHeader.c
useBadHeader.c:4: syntax error before "int"
useBadHeader.c: In function `main':
useBadHeader.c:6: `BoringStruct' undeclared (first use in this function)
useBadHeader.c:6: (Each undeclared identifier is reported only once
useBadHeader.c:6: for each function it appears in.)
useBadHeader.c:6: parse error before "bs"
useBadHeader.c:7: `bs' undeclared (first use in this function)

/home/daveti/Ctest/cCompiler: gcc -o useBadHeader.E -E useBadHeader.c
/home/daveti/Ctest/cCompiler: cat useBadHeader.E
......
extern void funlockfile (FILE *__stream) ;
# 679 "/usr/include/stdio.h" 3

# 2 "useBadHeader.c" 2
# 1 "badHeader.h" 1


typedef struct _boringStruct
{
 int a;
 int b;
 int c;
} BoringStruct
# 3 "useBadHeader.c" 2

int main()
{
 BoringStruct bs;
 bs.a = 1;
 bs.b = 2;
 bs.c = 3;
 printf( "a=%d, b=%d, c=%dn", bs.a, bs.b, bs.c);
 return 0;
}

/home/daveti/Ctest/cCompiler: cp useBadHeader.E useBadHeader.c
/home/daveti/Ctest/cCompiler: gcc -o useBadHeader useBadHeader.c
useBadHeader.c:4: syntax error before "int"
useBadHeader.c: In function `main':
useBadHeader.c:6: `BoringStruct' undeclared (first use in this function)
useBadHeader.c:6: (Each undeclared identifier is reported only once
useBadHeader.c:6: for each function it appears in.)
useBadHeader.c:6: parse error before "bs"
useBadHeader.c:7: `bs' undeclared (first use in this function)



/home/daveti/Ctest/cCompiler: cat useBadHeader_hack.c
#include <stdio.h>
#include "badHeader.h"

/* Hack */
;

int main()
{
 BoringStruct bs;
 bs.a = 1;
 bs.b = 2;
 bs.c = 3;
 printf( "a=%d, b=%d, c=%dn", bs.a, bs.b, bs.c);
 return 0;
}

/home/daveti/Ctest/cCompiler: gcc -o useBadHeader_hack useBadHeader_hack.c
/home/daveti/Ctest/cCompiler: ./useBadHeader_hack
a=1, b=2, c=3

/home/daveti/Ctest/cCompiler: gcc -o useBadHeader_hack.E -E useBadHeader_hack.c
/home/daveti/Ctest/cCompiler: cat useBadHeader_hack.E
......
extern void funlockfile (FILE *__stream) ;
# 679 "/usr/include/stdio.h" 3

# 2 "useBadHeader_hack.c" 2
# 1 "badHeader.h" 1


typedef struct _boringStruct
{
 int a;
 int b;
 int c;
} BoringStruct
# 3 "useBadHeader_hack.c" 2


;

int main()
{
 BoringStruct bs;
 bs.a = 1;
 bs.b = 2;
 bs.c = 3;
 printf( "a=%d, b=%d, c=%dn", bs.a, bs.b, bs.c);
 return 0;
}



[root@localhost cCompiler]# ll
total 32
drwxr-xr-x 2 root root 4096 May 29 01:09 .
drwxr-xr-x 3 root root 4096 May 29 01:06 ..
-rw-r--r-- 1 root root  134 May 29 01:07 badHeader.h
-rw-r--r-- 1 root root  212 May 29 01:09 useBadHeader.c
[root@localhost cCompiler]# splint *
Splint 3.1.1 --- 19 Jul 2006

badHeader.h:9:1: Parse Error. (For help on parse errors, see splint -help
 parseerrors.)
*** Cannot continue.
[root@localhost cCompiler]#
Posted in Programming, Static Code Analysis, Stuff about Compiler | Tagged , , | Leave a comment

csve – csv file evolver

Csv file, short for Comma Separated Version file, is usually a easy and common way to record [field:value] in multiple lines with all fields and values separated by comma, which is the basic data file of Microsoft Excel, as well as configuration files for other softwares. Internally, we have a software called H.248 simulator using a few csv files as configuration file. However, because of continuous feature development, more and more fields are added into existing csv file, even new csv file. Then it would be painful and time consuming to upgrade old csv files into new ones when binary file is changed into the latest. That is why csve is generated – to evolve old configured csv files based on new template csv files.

Project Name: csve
Destination: Csv file evolver
Language: Python
IDE: Vi
Project Web: http://github.com/daveti/csve
Git Read Only: https://github.com/daveti/csve.git

Core algorithm of csve - MO (matrix operation):

# Basic infra of csv file - src/new
# This is comment line - new
A,B,C                                 A,d11,d21,d31,d41
d11,d12,d13             =>              B,d12,d22,d32,d42
d21,d22,d23     reverseMatrixList       C,d13,d23,d33,d43
d31,d32,d33
d41,d42,d43

                + (evolve from src to dst)

# Basic infra of csv file - dst/old
# This is comment line - old
A,C                                     A,e11,#e21,e31
e11,e12                 =>              C,e12,e22,e32
#e21,e22        reverseMatrixList
e31,e32

                = (new dst)

# Basic infra of csv file - src/new
# This is comment line - new
A,B,C                                   A,e11,#e21,e31
e11,d12,e12             =>              B,d12,d22,d32
#e21,d22,e22    reverseMatrixList       C,e12,e22,e32
e31,d23,e32
Posted in Dave's Tools, Programming | Tagged , , | Leave a comment

cccmt – Coverity code complexity metrics tool

cccmt is used to parse the METRICS.errors.xml generated by cov-analyze of Coverity to produce a Code Complexity Metrics (CCM) report of different functions. SAX is used to parse XML file instead of DOM as the XML file may be very large. Unfortunately, original METRICS.errors.xml is not well formatted. To make the parsing smoothly, we need to add <daveti> and </daveti> at the very beginning and ending in the XML file as the only root element. The other point is cccmt.jar is compiled via JDK 1.6 – may report error if it is run via JDK 1.5 and lower version.

Project Name: cccmt
Destination: Coverity Code Complexity Metrics Tool
Language: Java
IDE: NetBeans 7.1
Project Web: http://github.com/daveti/cccmt
Git Read Only: https://github.com/daveti/cccmt.git

/* Example METRICS.errors.xml */
/home/daveti/cccmt: cat abcde.xml
<daveti>
<fnmetric>
<file>/home_nbu/daveti/R2608/ipm_cov_off_bsub/glob/src/BSPlinux/sharedIP.c</file>
<fnmet>task_dup_chk;4;92;63;287;64;20;35;43314.7;0.952626;;;2130</fnmet>
</fnmetric>
<fnmetric>
<file>/home_nbu/daveti/R2608/ipm_cov_off_bsub/glob/src/BSPlinux/sharedIP.c</file>
<fnmet>check_ip_plumbing;4;63;46;193;70;19;23;46117.5;1.02191;;;1309</fnmet>
</fnmetric>
<fnmetric>
<file>/home_nbu/daveti/R2608/ipm_cov_off_bsub/glob/src/BSPlinux/sharedIP.c</file>
<fnmet>check_ip_tentative;3;68;47;217;82;23;26;52530;1.08282;;;1565</fnmet>
</fnmetric>
<fnmetric>
<file>/home_nbu/daveti/R2608/ipm_cov_off_bsub/glob/src/BSPlinux/sharedIP.c</file>
<fnmet>audit_IP_plumbing;1;14;12;30;9;6;5;471.803;0.0288859;;;1518</fnmet>
</fnmetric>
<fnmetric>
<file>/home_nbu/daveti/R2608/ipm_cov_off_bsub/glob/msi/BSPlinux/sharedIP.c</file>
<fnmet>audit_IPV6_duplicate_ip;2;30;22;196;26;17;12;8491.05;0.230687;;;1799</fnmet>
</fnmetric>
<fnmetric>
<file>/home_nbu/daveti/R2608/ipm_cov_off_bsub/glob/cdma/BSPlinux/sharedIP.c</file>
<fnmet>set_shared_ip_state_up;1;26;19;102;40;19;10;11946.4;0.309893;;;339</fnmet>
</fnmetric>
<fnmetric>
<file>/usr/include/sys/stat.h</file>
<fnmet>set_shared_ip_state_up;1;26;19;102;40;19;10;11946.4;0.309893;;;339</fnmet>
</fnmetric>
</daveti>
/home/daveti/cccmt:

Posted in Dave's Tools, Programming, Static Code Analysis | Tagged , , , , , , | Leave a comment

itevad – How to write your own protocol and its stack – part 9

Previous_Part_8

Now we are moving to the last post of ‘itevad’ – writing a stack for decoding text format of Itevad Protocol using Flex and Bison as well as C as a main function. Being different with previous Flex and Bison, our itevadTxtDecoderBin will scan the input from a file instead of stdout. Moreover, itevadTxtDecoderBin would parse the text msg into Itevad msg structure generated by ASN.1 compiler in binary encoding and then print it using XML. Another important thing we need to care about is the flex build issue when certain option is added. The finally solution for this build error is to upgrade your Flex into latest – 2.5.35, my here. Wish you like the series of Itevad’s post. Any question, please feel free to leave a msg or email. God bless us:)

/* Flex + Bison as stack of decoding Text Itevad Protocol */

/* Flex - as scanner */
[root@localhost itevad2]# cat itevadTxtFlex2.l
/*
Itevad Protocol Text Encoding Lexical Analyzer Example
April 18, 2012
http://daveti.blog.com
Reference: itevad.abnf
NOTE: This is a Flex file NOT Lex!
*/

%{
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "ItevadMessage.h"
#include "itevadTxtBison2.tab.h"
%}

/* Configure our lexer */
%option stack noyywrap case-insensitive
%option header-file="itevadTxtFlex2.h"

/*
NOTE: for option 'reentrant', all the generated internal calls of flex would
have a argument called 'yy_scan yy_scanner'! To avoid this, we will not
add option for 'reentrant'.
*/

%%

"." { return DOT; } /* Dot */
"[" { return LSB; } /* Left square bracket */
"]" { return RSB; } /* Right square bracket */
"=" { return EQUAL; } /* Equal */
":" { return COLON; } /* Colon */
"{" { return LBRKT; } /* Left brace */
"}" { return RBRKT; } /* Right brace */
"/" { return SLASH; } /* Slash */
"Itevad" { return ItevadToken; } /* Itevad Token */
"Transaction" { return TransToken; } /* Transaction Token */
"Reply" { return ReplyToken; } /* Reply Token */
"Ask" { return AskToken; } /* Ask Token */
"Answer" { return AnswerToken; } /* Answer Token */
"(.)*" { printf("Debug - quoted string: %sn", yytext);
 /* Delete the prefix and suffix double quotes */
 char *content = strndup( (yytext+1), (strlen(yytext)-2));
 yylval.stringVal = content;
 printf("flex-debug: quotedString: %sn", yylval.stringVal);
 return quotedString;
 } /* Quoted String */
[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3} { /* IPv4 address handling here */
 printf("Debug - IPv4 address: %sn", yytext);
 /* Try to convert the IPv4 format into octet string format
 * to help the parser for value assignment.
 * "49.49.49.49" --> "1111"
 * char '1' has ASCII code "49"
 * NOTE: we do have a bug here as NOT all the IPv4 address
 * could be translated into "xxxx" format string!!!!!!!!
 */
 char *ipv4Address = strdup( yytext);
 char *octecString = calloc( 1, 5);
 int i = 0;
 unsigned char value = '';
 char *headPtr = ipv4Address;
 char *ptr = ipv4Address;
 while ( *ptr != '')
 {
 if ( *ptr == '.')
 {
 *ptr = '';
 value = (unsigned char)strtoul( headPtr, NULL, 10);
 i += snprintf( octecString+i, 5-i, "%c", value);
 headPtr = ptr + 1;
 }
 ptr++;
 }
 /* Convert the last octec char */
 value = (unsigned char)strtoul( headPtr, NULL, 10);
 i += snprintf( octecString+i, 5-i, "%c", value);
 yylval.stringVal = octecString;
 printf("flex-debug: IPv4Address: %sn", yylval.stringVal);
 return IPv4Address;
 } /* IPv4 Address */
[0-9]+ { printf("Debug - number: %sn", yytext);
 yylval.ulongVal = strtoul(yytext, NULL, 10);
 printf("flex-debug: NUMBER: %lun", yylval.ulongVal);
 return NUMBER;
 } /* Number */
[ trnv] { /* Ignore */ } /* White spaces */
. { printf( "Decode failure at %cn", *yytext); }

%%
[root@localhost itevad2]#


/* Flex 2.5.4 bug */
[root@localhost itevad2]# flex itevadTxtFlex2.l
"itevadTxtFlex2.l", line 19: unrecognized %option: header-file
"itevadTxtFlex2.l", line 20: unrecognized %option: reentrant
[root@localhost itevad2]#

/* Upgrade to Flex 2.5.35 */
[root@localhost flex-2.5.35]# which flex
/usr/local/bin/flex
[root@localhost flex-2.5.35]# flex -V
flex 2.5.35
[root@localhost flex-2.5.35]#
/* Bison - as parser */
[root@localhost itevad2]# cat itevadTxtBison2.y
/*
Itevad Protocol Text Encoding Sematic Analyzer Example
April 18, 2012
http://daveti.blog.com
Reference: itevad.abnf
NOTE: This is a Bison file NOT Yacc!
*/

%{
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "ItevadMessage.h"

extern int yylex();
%}

/* Token list */
%token SLASH LBRKT RBRKT COLON EQUAL NUMBER LSB RSB quotedString
%token ItevadToken TransToken ReplyToken AskToken AnswerToken
%token DOT IPv4Address

/* Type list */
%type <ulongVal> version transactionId portNumber NUMBER
%type <stringVal> quotedString IPv4Address
%type <itevadMsgVal> itevadMessage
%type <ipv4AddressVal> ipv4Address
%type <messageBodyVal> messageBody
%type <messageReqVal> messageRequest
%type <messageRepVal> messageReply
%type <askContentVal> askContent
%type <answerContentVal> answerContent

%union
{
 unsigned long ulongVal;
 char * stringVal;
 ItevadMessage_t itevadMsgVal;
 IP4Address_t ipv4AddressVal;
 MessageBody_t messageBodyVal;
 MessageRequest_t messageReqVal;
 MessageReply_t messageRepVal;
 AskContent_t askContentVal;
 AnswerContent_t answerContentVal;
}

%parse-param { ItevadMessage_t *itevadMsg}
%start itevadMessage

%%

itevadMessage:
 ItevadToken SLASH version ipv4Address messageBody
 { $$.version = $3;
 $$.ip4Address = $4;
 $$.messageBody = $5;
 *itevadMsg = $$;
 }
 ;

version:
 NUMBER
 { printf("bison-debug: version: %lun", $1);
 $$ = $1;
 }
 ;

ipv4Address:
 LSB IPv4Address RSB
 { printf("bison-debug: IPv4Address-$2: %sn", $2);
 OCTET_STRING_t myAddress;
 memset( &myAddress, 0, sizeof(OCTET_STRING_t));
 if ( OCTET_STRING_fromString( &myAddress, $2) == -1)
 {
 yyerror( "OCTET_STRING_fromString failure for ip4Address.addressn");
 }
 $$.address = myAddress;
 }
 | LSB IPv4Address RSB COLON portNumber
 { printf("bison-debug: IPv4Address-$2: %sn", $2);
 OCTET_STRING_t myAddress;
 memset( &myAddress, 0, sizeof(OCTET_STRING_t));
 if ( OCTET_STRING_fromString( &myAddress, $2) == -1)
 {
 yyerror( "OCTET_STRING_fromString failure for ip4Address.addressn");
 }
 $$.address = myAddress;
 $$.portNumber = &($5);
 }
 ;

portNumber:
 NUMBER
 { printf("bison-debug: portNumber: %lun", $1);
 $$ = $1;
 }
 ;

messageBody:
 messageRequest
 { $$.present = MessageBody_PR_messageRequest;
 $$.choice.messageRequest = $1;
 }
 | messageReply
 { $$.present = MessageBody_PR_messageReply;
 $$.choice.messageReply = $1;
 }
 ;

messageRequest:
 TransToken EQUAL transactionId LBRKT askContent RBRKT
 { INTEGER_t myTransId;
 memset( &myTransId, 0, sizeof(INTEGER_t));
 if ( asn_long2INTEGER( &myTransId, $3) == -1)
 {
 yyerror( "asn_long2INTEGER failure for transId in messageRequestn");
 }
 $$.transactionId = myTransId;
 $$.askContent = $5;
 }
 ;

messageReply:
 ReplyToken EQUAL transactionId LBRKT answerContent RBRKT
 { INTEGER_t myTransId;
 memset( &myTransId, 0, sizeof(INTEGER_t));
 if ( asn_long2INTEGER( &myTransId, $3) == -1)
 {
 yyerror( "asn_long2INTEGER failure for transId in messageReplyn");
 }
 $$.transactionId = myTransId;
 $$.answerContent = $5;
 }
 ;

transactionId:
 NUMBER
 { printf("bison-debug: transactionId: %lun", $1);
 $$ = $1;
 }
 ;

askContent:
 AskToken EQUAL quotedString
 { printf("bison-debug: askContent-quotedString-$3: %sn", $3);
 AskContent_t myAskContent;
 memset( &myAskContent, 0, sizeof(OCTET_STRING_t));
 if ( OCTET_STRING_fromString( &myAskContent, $3) == -1)
 {
 yyerror( "OCTET_STRING_fromString failure for askContentn");
 }
 $$ = myAskContent;
 }
 ;

answerContent:
 AnswerToken EQUAL quotedString
 { printf("bison-debug: answerContent-quotedString-$3: %sn", $3);
 AnswerContent_t myAnswerContent;
 memset( &myAnswerContent, 0, sizeof(OCTET_STRING_t));
 if ( OCTET_STRING_fromString( &myAnswerContent, $3) == -1)
 {
 yyerror( "OCTET_STRING_fromString failure for answerContentn");
 }
 $$ = myAnswerContent;
 }
 ;

%%

int yyerror( const char *msg)
{
 fprintf( stderr, "yyerror: %sn", msg);
}

[root@localhost itevad2]#
/* main - stack of Itevad decoder */
[root@localhost itevad2]# cat itevadTxtDecoder.c
/*
Itevad Protocol Text Decoder Example
April 18, 2012
http://daveti.blog.com
*/

#include <stdio.h>
#include <sys/types.h>
#include "itevadTxtFlex2.h"
#include "ItevadMessage.h"

extern int yyparse( ItevadMessage_t *itevadMsg);

int main( int ac, char **av)
{
 char buf[ 1024] = {0}; /* Temp buffer - NOTE: may overflow...*/
 ItevadMessage_t *itevadMsg = NULL; /* Type to decode */
 int rval; /* Decoder return value */
 FILE *fp; /* Input file handler */
 size_t size; /* Number of bytes read */
 char *filename; /* Input file name */

 /* Require a single filename argument */
 if ( ac != 2)
 {
 fprintf( stderr, "Usage: %s <file.msg>n", av[ 0]);
 exit(64); /* better, EX_USAGE */
 }
 else
 {
 filename = av[ 1];
 }

 /* Open input file as read-only binary */
 fp = fopen( filename, "rb");
 if ( !fp)
 {
 perror( filename);
 exit(66); /* better, EX_NOINPUT */
 }

 /* Read up to the buffer size */
 size = fread( buf, 1, sizeof( buf), fp);
 fclose( fp);
 if ( !size)
 {
 fprintf( stderr, "%s: Empty or brokenn", filename);
 exit(65); /* better, EX_DATAERR */
 }

 /* Decode the input buffer as Itevad type */
 itevadMsg = calloc( 1, sizeof(ItevadMessage_t));
 if ( itevadMsg == NULL)
 {
 fprintf( stderr, "Memory allocation failure for itevadMsgn");
 exit(65);
 }

 printf("itevadTxtDecoderBin-debug: calling yy_scan_string() - buf:n"
 "%sn", buf);
 yy_scan_string( buf);

 printf("itevadTxtDecoderBin-debug: calling yyparse()n");
 rval = yyparse( itevadMsg);
 if ( rval != 0)
 {
 fprintf( stderr, "%s: Broken ItevadMessage Bison decodingn",
 filename);
 exit(65); /* better, EX_DATAERR */
 }

 /* Print the decoded Itevad type as XML */
 printf("itevadTxtDecoderBin-debug: calling xer_fprint()n");
 xer_fprint( stdout, &asn_DEF_ItevadMessage, itevadMsg);

 /* Decoding finished successfully */
 return 0;
}
[root@localhost itevad2]#
/* Generate and run itevadTxtDecoderBin */
[root@localhost itevad2]# bison -d itevadTxtBison2.y
[root@localhost itevad2]# flex itevadTxtFlex2.l
[root@localhost itevad2]# gcc -o flexBin lex.yy.c -lfl /* delete */
[root@localhost itevad2]# gcc -I. -o itevadTxtDecoderBin *.c -lfl
itevadTxtFlex2.l: In function ‘yylex’:
itevadTxtFlex2.l:38: warning: incompatible implicit declaration of built-in function ‘strndup’
TransactionId.c: In function ‘TransactionId_constraint’:
TransactionId.c:31: warning: this decimal constant is unsigned only in ISO C90
[root@localhost itevad2]# mv itevadTxtDecoderBin ItevadBin
mv: overwrite `ItevadBin/itevadTxtDecoderBin'? yes
[root@localhost itevad2]#

[root@localhost ItevadBin]# ./itevadTxtDecoderBin itevad_text.msg
itevadTxtDecoderBin-debug: calling yy_scan_string() - buf:
Itevad/1 [49.49.49.49]:7777
Transaction = 65535 {
        Ask = "who r you?"
}

itevadTxtDecoderBin-debug: calling yyparse()
Debug - number: 1
flex-debug: NUMBER: 1
bison-debug: version: 1
Debug - IPv4 address: 49.49.49.49
flex-debug: IPv4Address: 1111
Debug - number: 7777
flex-debug: NUMBER: 7777
bison-debug: portNumber: 7777
bison-debug: IPv4Address-$2: 1111
Debug - number: 65535
flex-debug: NUMBER: 65535
bison-debug: transactionId: 65535
Debug - quoted string: "who r you?"
flex-debug: quotedString: who r you?
bison-debug: askContent-quotedString-$3: who r you?
itevadTxtDecoderBin-debug: calling xer_fprint()
<ItevadMessage>
    <version>1</version>
    <ip4Address>
        <address>31 31 31 31</address>
        <portNumber>65535</portNumber>
    </ip4Address>
    <messageBody>
        <messageRequest>
            <transactionId>65535</transactionId>
            <askContent>who r you?</askContent>
        </messageRequest>
    </messageBody>
</ItevadMessage>
[root@localhost ItevadBin]#
Posted in Dave's Tools, H.248/MEGACO/EGCP, Programming, Stuff about Compiler | Tagged , , | Leave a comment

itevad – How to write your own protocol and its stack – part 8

Previous_Part_7

After writing a Flex standalone lexer, we are going to make it a little bit useful – adding parser, Bison. When Flex and Bison work together, standalone flex file needs some changes to cooperate with Bison, as you will see. Though the Bison looks much more weird than Flex, fortunately, we only cover the basic meaning of $$, $1 and etc, leaving recursion and conflict alone…

/* Flex + Bison to scan and parse the Text Itevad Protocol */

/* Flex - as scanner */
[root@localhost itevad2]# cat itevadTxtFlex.l
/*
Itevad Protocol Text Encoding Lexical Analyzer Example
April 6, 2012
http://daveti.blog.com
Reference: itevad.abnf
NOTE: This is a Flex file NOT Lex!
*/

%{
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "ItevadMessage.h"
#include "itevadTxtBison.tab.h"
%}

/* Configure our lexer */
%option stack noyywrap case-insensitive

%%

"." { return DOT; } /* Dot */
"[" { return LSB; } /* Left square bracket */
"]" { return RSB; } /* Right square bracket */
"=" { return EQUAL; } /* Equal */
":" { return COLON; } /* Colon */
"{" { return LBRKT; } /* Left brace */
"}" { return RBRKT; } /* Right brace */
"/" { return SLASH; } /* Slash */
"Itevad" { return ItevadToken; } /* Itevad Token */
"Transaction" { return TransToken; } /* Transaction Token */
"Reply" { return ReplyToken; } /* Reply Token */
"Ask" { return AskToken; } /* Ask Token */
"Answer" { return AnswerToken; } /* Answer Token */
"(.)*" { printf("Debug - quoted string: %sn", yytext);
 /* Delete the prefix and suffix double quotes */
 char *content = strndup( (yytext+1), (strlen(yytext)-2));
 yylval.stringVal = content;
 printf("flex-debug: quotedString: %sn", yylval.stringVal);
 return quotedString;
 } /* Quoted String */
[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3} { /* IPv4 address handling here */
 printf("Debug - IPv4 address: %sn", yytext);
 /* Try to convert the IPv4 format into octet string format
 * to help the parser for value assignment.
 * "49.49.49.49" --> "1111"
 * char '1' has ASCII code "49"
 * NOTE: we do have a bug here as NOT all the IPv4 address
 * could be translated into "xxxx" format string!!!!!!!!
 */
 char *ipv4Address = strdup( yytext);
 char *octecString = calloc( 1, 5);
 int i = 0;
 unsigned char value = '';
 char *headPtr = ipv4Address;
 char *ptr = ipv4Address;
 while ( *ptr != '')
 {
 if ( *ptr == '.')
 {
 *ptr = '';
 value = (unsigned char)strtoul( headPtr, NULL, 10);
 i += snprintf( octecString+i, 5-i, "%c", value);
 headPtr = ptr + 1;
 }
 ptr++;
 }
 /* Convert the last octec char */
 value = (unsigned char)strtoul( headPtr, NULL, 10);
 i += snprintf( octecString+i, 5-i, "%c", value);
 yylval.stringVal = octecString;
 printf("flex-debug: IPv4Address: %sn", yylval.stringVal);
 return IPv4Address;
 } /* IPv4 Address */
[0-9]+ { printf("Debug - number: %sn", yytext);
 yylval.ulongVal = strtoul(yytext, NULL, 10);
 printf("flex-debug: NUMBER: %lun", yylval.ulongVal);
 return NUMBER;
 } /* Number */
[ trnv] { /* Ignore */ } /* White spaces */
. { printf( "Decode failure at %cn", *yytext); }

%%
[root@localhost itevad2]#
/* Bison - as parser */
[root@localhost itevad2]# cat itevadTxtBison.y
/*
Itevad Protocol Text Encoding Sematic Analyzer Example
April 9, 2012
http://daveti.blog.com
Reference: itevad.abnf
NOTE: This is a Bison file NOT Yacc!
*/

%{
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "ItevadMessage.h"
%}

/* Token list */
%token SLASH LBRKT RBRKT COLON EQUAL NUMBER LSB RSB quotedString
%token ItevadToken TransToken ReplyToken AskToken AnswerToken
%token DOT IPv4Address

/* Type list */
%type <ulongVal> version transactionId portNumber NUMBER
%type <stringVal> quotedString IPv4Address
%type <itevadMsgVal> itevadMessage
%type <ipv4AddressVal> ipv4Address
%type <messageBodyVal> messageBody
%type <messageReqVal> messageRequest
%type <messageRepVal> messageReply
%type <askContentVal> askContent
%type <answerContentVal> answerContent

%union
{
 unsigned long ulongVal;
 char * stringVal;
 ItevadMessage_t itevadMsgVal;
 IP4Address_t ipv4AddressVal;
 MessageBody_t messageBodyVal;
 MessageRequest_t messageReqVal;
 MessageReply_t messageRepVal;
 AskContent_t askContentVal;
 AnswerContent_t answerContentVal;
}

%start itevadMessage

%%

itevadMessage:
 ItevadToken SLASH version ipv4Address messageBody
 { $$.version = $3;
 $$.ip4Address = $4;
 $$.messageBody = $5;
 }
 ;

version:
 NUMBER
 { printf("bison-debug: version: %lun", $1);
 $$ = $1;
 }
 ;

ipv4Address:
 LSB IPv4Address RSB
 { printf("bison-debug: IPv4Address-$2: %sn", $2);
 OCTET_STRING_t myAddress;
 memset( &myAddress, 0, sizeof(OCTET_STRING_t));
 if ( OCTET_STRING_fromString( &myAddress, $2) == -1)
 {
 yyerror( "OCTET_STRING_fromString failure for ip4Address.addressn");
 }
 $$.address = myAddress;
 }
 | LSB IPv4Address RSB COLON portNumber
 { printf("bison-debug: IPv4Address-$2: %sn", $2);
 OCTET_STRING_t myAddress;
 memset( &myAddress, 0, sizeof(OCTET_STRING_t));
 if ( OCTET_STRING_fromString( &myAddress, $2) == -1)
 {
 yyerror( "OCTET_STRING_fromString failure for ip4Address.addressn");
 }
 $$.address = myAddress;
 $$.portNumber = &($5);
 }
 ;

portNumber:
 NUMBER
 { printf("bison-debug: portNumber: %lun", $1);
 $$ = $1;
 }
 ;

messageBody:
 messageRequest
 { $$.present = MessageBody_PR_messageRequest;
 $$.choice.messageRequest = $1;
 }
 | messageReply
 { $$.present = MessageBody_PR_messageReply;
 $$.choice.messageReply = $1;
 }
 ;

messageRequest:
 TransToken EQUAL transactionId LBRKT askContent RBRKT
 { INTEGER_t myTransId;
 memset( &myTransId, 0, sizeof(INTEGER_t));
 if ( asn_long2INTEGER( &myTransId, $3) == -1)
 {
 yyerror( "asn_long2INTEGER failure for transId in messageRequestn");
 }
 $$.transactionId = myTransId;
 $$.askContent = $5;
 }
 ;

messageReply:
 ReplyToken EQUAL transactionId LBRKT answerContent RBRKT
 { INTEGER_t myTransId;
 memset( &myTransId, 0, sizeof(INTEGER_t));
 if ( asn_long2INTEGER( &myTransId, $3) == -1)
 {
 yyerror( "asn_long2INTEGER failure for transId in messageReplyn");
 }
 $$.transactionId = myTransId;
 $$.answerContent = $5;
 }
 ;

transactionId:
 NUMBER
 { printf("bison-debug: transactionId: %lun", $1);
 $$ = $1;
 }
 ;

askContent:
 AskToken EQUAL quotedString
 { printf("bison-debug: askContent-quotedString-$3: %sn", $3);
 AskContent_t myAskContent;
 memset( &myAskContent, 0, sizeof(OCTET_STRING_t));
 if ( OCTET_STRING_fromString( &myAskContent, $3) == -1)
 {
 yyerror( "OCTET_STRING_fromString failure for askContentn");
 }
 $$ = myAskContent;
 }
 ;

answerContent:
 AnswerToken EQUAL quotedString
 { printf("bison-debug: answerContent-quotedString-$3: %sn", $3);
 AnswerContent_t myAnswerContent;
 memset( &myAnswerContent, 0, sizeof(OCTET_STRING_t));
 if ( OCTET_STRING_fromString( &myAnswerContent, $3) == -1)
 {
 yyerror( "OCTET_STRING_fromString failure for answerContentn");
 }
 $$ = myAnswerContent;
 }
 ;

%%

int main( int argc, char **argv)
{
 yyparse();
 return 0;
}

int yyerror( const char *msg)
{
 fprintf( stderr, "yyerror: %sn", msg);
}

[root@localhost itevad2]#


/* Generate and run itevadFlexBisonBin */
[root@localhost itevad2]# bison -d itevadTxtBison.y
[root@localhost itevad2]# flex itevadTxtFlex.l
[root@localhost itevad2]# gcc -I. -o itevadTxtFlexBisonBin *.c -lfl
itevadTxtFlex.l: In function 342200230yylex342200231:
itevadTxtFlex.l:37: warning: incompatible implicit declaration of built-in function 342200230strndup342200231
TransactionId.c: In function 342200230TransactionId_constraint342200231:
TransactionId.c:31: warning: this decimal constant is unsigned only in ISO C90
[root@localhost itevad2]# ./itevadTxtFlexBisonBin
Itevad/1 [49.49.49.49]:7777
Transaction = 65535 {
        Ask = "who r you?"
}
Debug - number: 1
flex-debug: NUMBER: 1
bison-debug: version: 1
Debug - IPv4 address: 49.49.49.49
flex-debug: IPv4Address: 1111
Debug - number: 7777
flex-debug: NUMBER: 7777
bison-debug: portNumber: 7777
bison-debug: IPv4Address-$2: 1111
Debug - number: 65535
flex-debug: NUMBER: 65535
bison-debug: transactionId: 65535
Debug - quoted string: "who r you?"
flex-debug: quotedString: who r you?
bison-debug: askContent-quotedString-$3: who r you?

[root@localhost itevad2]#

Posted in Dave's Tools, H.248/MEGACO/EGCP, Programming, Stuff about Compiler | Tagged , , | Leave a comment