Sunday, November 25, 2007

Smart Debugger











NAME


Devel::sdb - Smart Debugger







SYNOPSIS


perl -d:sdb myscript.pl






DESCRIPTION


Devel::sdb is an enhanced version of Perl's (v5.8.8) powerful interactive debugger. The GUI version ptkdb does a very good job already. However, smart debugger takes different approach to problem solving and focuses on non GUI situations. Some of the features added to this version are data rendering, flexible breakpoints, zoompoints, custom mode for debugging fork process and syntax highlighting.







FEATURES



a. Data rendering.
b. Code window/markers
c. Flexible breakpoints.
d. Zoom points for bookmarks/line regex.
e. Debugging boundaries.
f. Custom mode for debugging forked process.
g. Syntax highlight
h. Debug preferences for instant debugging.





USAGE


Usage 1.




perl -d:sdb myscript.pl

Usage 2.



setenv SDB_BPTS "new init"
perl -d:sdb myscript.pl

Usage 3.



setenv SDB_PREF "$HOME/.sdb/pref.conf"

perl -d:sdb myscript.pl





COMMANDS



# CODE
set c[aller] set caller segment
set noc[aller] unset caller segment
set p[ad] set padding code
set nop[ad] unset padding code
set q[uick] set quick exit
set noq[uick] unset quick exit
set cw[size] -pre,post set code window size


# DATA
set dr[ender] set data render
set nodr[ender] unset datarender
set dm[ode] 1[1-9] datadumper,depth
set dm[ode] 2[1-9] dumpvar,depth



# BREAKPOINT
set b[reakpoint] expr set sdb style breakpoints
set b[reakpoint] compile expr set sdb style "b compile"
set b[reakpoint] postpone expr set sdb style "b postpone"
set b[reakpoint] load expr set sdb style "b load"
list b[reakpoint] display the recent sdb style breakpoint
list p[references] display the preferences initialised


# ZOOMPOINT
set zw[size] -pre,post set zoom window size
z /pattern - zoom to pattern with zwindow
z - zoom next



# ENTER MAP (by default enter mapped to 's')
set j[ump] set enter key map to 'c'
set n[ext] set enter key map to 'n'
set s[ingle] set enter key map to 's'
set z[oom] set enter key map to 'z'





PREFERENCES (DEFAULT)



Default preferences when SDB_CONF is unset.


$config = {
'code' => { # Code
'window' => '-8,20', # c.1
'zwindow' => '-1,0', # c.2
'caller' => '0', # c.3
'comments' => '1', # c.4
'warnings' => '1', # c.5
'padcode' => '1', # c.6
'quickexit'=> '1', # c.7
},
'data' => { # Data
'render' => '1', # d.1
'subexp' => '1', # d.2
'mode' => '10', # d.3
'skip' => [ '$_', '@_', '$self' ], # d.4
},
'breakpt' => { # Breakpoints
'b' => [ '', ], # b.1
'b_load' => [ '', ], # b.2
'b_compile' => [ '', ], # b.3
'b_postpone'=> [ '', ], # b.4
},
'boundary' => { # Boundary
'show' => { # Show
'files' => [ '', ], # bs.1
'packs' => [ '', ], # bs.2
'subs' => [ '', ], # bs.3
},
'skip' => { # Skip
'files' => [ '', ], # bs.3
'packs' => [ '', ], # bs.4
'subs' => [ '', ], # bs.5
},
},
'forktty' => { # Fork
'mode' => 0, # f.1
'tmpdir' => "$ENV{HOME}/.sdb/fork/", # f.2
},
'syntax' => { # Syntax
'mode' => 0, # s.1
'tmpdir' => "$ENV{HOME}/.sdb/syntax/", # s.2
'command' => 'cat %f',
},
};






ENVIRONMENT



SDB_BPTS "new init"
SDB_PREF "$HOME/.sdb/pref.conf'






INSIGHT






Smart Debugger?


No program runs at the first time. So we need to debug !!


Nobodys' code is easy to understand by others. So we need help !!


What tools we have to solve these universal problems?
Debugger. Yes. We use debuggers, because it works out of the box, so that no changes required in the code. but due to complexity in the usage, most people migrated to use ``print'' statements as a debugging mechanism.


So, how to fix the debugger?
Most debuggers come with powerful features, but you don't want to use all most of the time. More importantly, a debugger should seem free handed, as though you only need the enter key. Let the debugger render the data, code flow, code markers, caller to give more insight to the code, data and code flow. Our actions should only be to observe and understand. Also, we should be able use the all of the features, only when there is uncertanity.


Smart debugger takes a closer look at the very purpose of debugging and focussed on simple usage. It unleashes the debugger's potential to do smart things. Smart debugger is more than debugger, it's a code walk through tool. It emphasize the concepts on data rendering and zoompoints, because it understands most common needs. So it provides details proactively, to help you understand quickly.







Want more insight to the data?


Ans. Data Rendering


Data is the primary element which determines code flow, and we use debuggers to trace the same. Smart Debugger has a tiny lexical parser that works on a single line. It renders automatically the ``greedy'' variable, sub expressions and the values. Most GUI debuggers provide data trace (not rendering), which allows you to see the stack of variables, globals or show values on variable mouseovers. They don't, however, render the execution context specfic data. An execution context for code at any of time is a single execution line with lvalue and rvalue and expressions in the same. So, rendering data for only what you see in the execution line will give better insight to the data flow.



example code:
$foo = 0;
$bar = 3;


if($foo == 2 and $bar == 3) {
print "im good \n";
} else {
print "im bad \n";
}


Common Debugger:



a. hey, im currently here.
=> if($foo == 2 & $bar == 3) {
b. user types keystrokes to go the next execution.
c. sometimes user types and check values in $foo, $bar
d. hey, im currently here
=> print "im bad \n";

Smart Debugger:




-----( DATA )------
$bar is
3


$foo is
0


-----( CODE )------
t1.pl:.:1 $foo = 0;
t1.pl:.:2 $bar = 3;
t1.pl:.:3
t1.pl:.:4=> if($foo == 2 and $bar == 3) {
t1.pl:.:5 print "im good \n";
t1.pl:.:6 } else {
t1.pl:.:7>> print "im bad \n";
t1.pl:.:8 }
t1.pl:.:9



a. hey, i have seen this line "=>" (data line)
b. these are the variables i see in the line and their values.
c. currently i see perl has control into this line ">>" (control line)
d. take a look & proceed.

Now you know at first look. $foo is 0, and that's the reason it
traversed into the else block. This is the very purpose of debugger: to
provide the necessary details quickly.


The purpose of the code walk through tool is also the same.
you will understand the code by correlating data
with code. In most cases you may not need comments and logs
to understand the code. Its just the data, code and the correlation.





Data Segment



-----( DATA )------
$node->{left} is
{
'left' => {
'value' => 'l',
'weight' => 6
},
'right' => {
'value' => 'o',
'weight' => 6
},
'value' => 'lo',
'weight' => 12
}




Greedy Variable


A greedy variable is not a variable itself. It is a variable expression. Smart debugger is greedy when looking for variable in the execution line.



Example #1: Simple
############################## SMART DEBUGGER ##############################
-----( DATA )------
$hash->[$one]{$two}{$three} is (greedy variable)
$hash->[$one]{$two}{3} is (sub expressions)
$hash->[$one]{2}{3} is (sub expressions)
$hash->[1]{2}{3} is (sub expressions)
5


-----( CODE )------
n6.pl:.:1 $one = 1;
n6.pl:.:2 $two = 2;
n6.pl:.:3 $three = 3;
n6.pl:.:4
n6.pl:.:5
n6.pl:.:6=> $hash->[$one]{$two}{$three} = 5;
n6.pl:.:7>> print ;
n6.pl:.:8



Example #2: Complex
############################## SMART DEBUGGER ##############################
-----( DATA )------
@old{ @hash{ @{$prefix->[$one]}, 3 }} is
@old{ @hash{ @{$prefix->[1]}, 3 }} is
@old{ @hash{ @{ARRAY(0x8288c8c)}, 3 }} is
@old{ @hash{ 2 1, 3 }} is
@old{ 2 1 3} is
3,
4,
5


-----( CODE )------
n7.pl:.:1 use Data::Dumper;
n7.pl:.:2 @hash { 1, 2, 3 } = ( 1, 2, 3 );
n7.pl:.:3 $prefix = [ 1, [2, 1]];
n7.pl:.:4 $one = 1;
n7.pl:.:5=> @old { @hash { @{$prefix->[$one]}, 3 }} = (3,4,5);
n7.pl:.:6>> print;


Variables in this line are :
%old, %hash, $prefix, $one.



Greedy variable is
@old{ @hash{ @{ $prefix->[$one] }, 3 }}

Smart Debugger renders only the greedy variables, to give more meaning to the context. It also handles the {}, [] contructs as a blackbox, to avoid complexity and for faster processing.



Greedy:
@old{ @hash{ @{ $prefix->[$one] }, 3 }} is
1,
2,
3

Now, with this you will know the value of greedy variable. but still we don't know the value of below.



$one
$prefix->[$one]
@{ $prefix->[$one] }
@hash { @{ $prefix->[$one] }, 3}



Sub Expressions Render:
@old{ @hash{ @{ $prefix->[$one] }, 3 }} is
@old{ @hash{ @{ $prefix->[1] }, 3 }} is
@old{ @hash{ @{ ARRAY(0x84782f0) }, 3 }} is
@old{ @hash{ 2 1, 3 }} is
@old{ 2 1 3} is
3,
4,
5

Sub expressions are used to render individual variables in a greedy variable. Note that not all uncommon constructs render properly, but it doesn't affect the value of greedy variable.


Note: Smart debugger tries to render the feasible expressions but it cannot ensure the meaning of the construct. So, always check the greedy variable and value.


you can turn on/off the sub expressions rendering using {data}{subexp} in preferences.



Configuration



preferences (static)
'data' => { # Data
'render' => '1', # d.1
'subexp' => '1', # d.2
'mode' => '10', # d.3
'skip' => [ '', ], # d.4
},


commands (dynamic)
set dr
set nodr
set dm 10 ( 1 is data dumper, 0 is infinite depth)
set dm 14 ( 1 id data dumper, 4 is depth)
set dm 20 ( 2 is dumpvar , 0 is infinite depth)
set dm 24 ( 2 is dumpvar , 4 is depth)






Want more insight to the code?


Ans. Code window/markers


Code window is used to represent the current execution line and window of lines around the execution line It enables a quick grasp of the execution and also provides two markers: ``=>'' data line, ``>>'' control line to indicate the code flow. Data rendering works on ``=>'' marked data line.




Code window size?



Most of the GUI debuggers are emulated inside the editor. So, the current execution line is represented by marking the line inside the editor. This enables a quick grasp of where the executions are happening. Where as the terminal debugger does not give that feel, but provides simpler methods to achieve it. This version address the same to bring the feel closer to GUI representation of code, so we are never lost during the debugging process. It also features two markers ``=>'' data line, ``>>'' control line which helps in most cases of conditional looping to understand which was the last execution line, and where the current executing line is.




Code markers?



Debugger Current "=>" is Perl's Last
Debugger Next ">>" is Perl's Current
please DO NOT remember this mapping.


Just remember that debugger is sitting between Perl's last exec and current exec line. The debugger is always one step behind perl, but knows what is coming down the pipeline. SDB debugger can deal with only what Perl has already done, but it knows what Perl is currently doing.



lets give some name to the markers now.

``=>'' data line - indicates the line ``debugger'' dealing currently.


``>>'' control line - indicate the line ``perl'' dealing currently, (for debugger, this is next)



example:



1. $foo = 0;
2. if($foo == 2) {
3. print "im good \n";
4. } else {
5. print "im bad \n";
6. }


a. perl. executed line 1, waiting in line 2.
b. debugger
line 1 is completed (lvalue accomplised) show the details for the same "=>"
line 2 is coming to me next ( ">>" )



1.=> $foo = 0;
2.>> if($foo == 2) {
3. print "im good \n";
4. } else {
5. print "im bad \n";
6. }


c. perl. executed line 2, waiting in line 5.
d. debugger
line 2 is completed (lvalue accomplised) show the details for the same "=>"

line 5 is coming to me next ( ">>" )


1. $foo = 0;
2.=> if($foo == 2) {
3. print "im good \n";
4. } else {
5.>> print "im bad \n";
6. }


This is particularly useful for understanding the code flow. Since the debugger is one step behind but knows what is coming next, it is easier to represent the data and code markers to understand easily the change in code flow.




Caller Segment



-----( CALLER )------
huffman-example:51:main::huff_hash
huffman:56:main::huff_hash_subtree



Code Segment (without padding)



-----( CODE )------
sub huff_hash_subtree {
my $node = shift;
my $hash = shift;
my $prefix = shift;

=> if( $node->{left} ) {
>> huff_hash_subtree( $node->{left}, $hash, [ @$prefix, 0 ] );
huff_hash_subtree( $node->{right}, $hash, [ @$prefix, 1 ] );
} else {
$hash->{$node->{value}} = $prefix;
}
}

sub huff_hash {
my $tree = shift;
my $hash = shift;
%$hash = ( );

huff_hash_subtree( $tree, $hash, [] );
}






Do you want to breakpoints using regex?


Ans. Flexible breakpoints (SDB_BPTS)


The debugger provides breakpoints to get to a debugging area more quickly. SDB provides flexible methods of setting breakpoints using regex rather than fixed string. It matches the regex with the subroutine lookup table and sets breakpoints to all matching subs. (Note: this doesn't help if you are setting breakpoints where your subroutines are being built dynamically, during runtime. You may still need to use basic breakpoints to set the same, but now less frequently than with other debuggers).



Configuration



environ
SDB_BPTS "new init DBI::select"


commands (dynamic)
S ( to list all subroutines)
set b new init (to set sdb style breakpoints)
list b (to list recently set sdb style breakpoints)


Note: SDB_BPTS overrides the {breakpt}{b} in preferences


preferences



'breakpt' => { # Breakpoints
'b' => [ '', ], # b.1
'b_load' => [ '', ], # b.2
'b_compile' => [ '', ], # b.3
'b_postpone'=> [ '', ], # b.4
},
# b.1 sdb style flexible breakpoints for subroutine
# b.2 sdb style flexible breakpoints for "b load"
# b.3 sdb style flexible breakpoints for "b compile"

# b.4 sdb style flexible breakpoints for "b postpone"





Getting bored with breakpoints?


Ans. Zoompoints


Breakpoints work on subroutine names and line numbers. Zoompoints work on regex matched lines and surrounding lines (bookmarks). This is an extremely useful runtime feature when you want to reach a particular code location instantly. GUI debuggers provide instant breakpoint settings by clicking on execution lines. Zoompoints are created for the same purpose, but their usage is more feature rich.



EXAMPLE:



zoompoint works the same way if you are able to do the below
b /pat (set a breakpoint when a line matches with "pat")
c (continue to that "pat")

Differences between breakpoint & zoompoint?



Breakpoints

breakpoints are used to set markers on subroutines or line numbers so you may jump into the code to debug quickly.




illustration: go to new york city (subroutine)


illustration: go to zipcode, new york city (line no)


Zoompoints

zoompoints work the same way, but work on the actual line (regex), or surrounding lines. It doesn't set markers, but scans for the regex in runtime.



illustration: stop me if you find starbucks on the way (line regex)



illustration: stop me if you find times square on the way (bookmark),
because i know there is a starbucks near by.


Illustration:


sub newyorkcity {
my ($var) = @_;
foreach $lane (1..100) {
$string = $lane . 'something';
}
$starbucks = "starbucks in newyork, 21st st\n";
$starbucks = "starbucks in newyork, 22nd st\n";
$macys = "10";
@sears = qw(1 2 3 4);
# TIMES SQUARE
$starbucks = "starbucks in newyork, 47th st\n";
print "$starbucks \n";
}



sub sanjosecity {
my ($var) = @_;
foreach $lane (1..100) {
$string = $lane . 'something';
}
$macys = "10";
@sears = qw(1 2 3 4);
$starbucks = "starbucks in sanjose, 20th st\n";
print "$starbucks \n";
}


sanjosecity();
newyorkcity();

Let's start with breakpoints ?




Lets assume, that you wanted to go to starbucks on 47nd st, new york city. So
> b newyorkcity
> c
> s or n or set breakpoint set line no of starbucks
to reach to the line.

Let's check with zoompoints ?



simple zoompoint to the above will be

case a.




set zw 0,0
z /starbucks
>> $starbucks = "starbucks in sanjose, 20th st\n";
z (zoom repeatedly)
>> $starbucks = "starbucks in newyork, 21st st\n";
>> $starbucks = "starbucks in newyork, 22nd st\n";
>> $starbucks = "starbucks in newyork, 47th st\n";


Now, you know why you couldn't hit the starbucks on 47th st directly, because the pattern is abstract and not expressive. So it took more steps to reach there.


case b.



set zw 0,0
z /starbucks in newyork
>> $starbucks = "starbucks in newyork, 21st st\n";
>> $starbucks = "starbucks in newyork, 22nd st\n";
>> $starbucks = "starbucks in newyork, 47th st\n";


This time you are more expressive and you hit the city, at least. It's also true that you cannot always be more expressive like ``starbucks in newyork, 47th st''. People want to be abstract and still get relevant results, so see case c.


case c.




set zw -1,0
z /TIMESQUARE
>> $starbucks = "starbucks in newyork, 47th st\n"

sometimes if you are not able to more expressive, you could use the bookmarks (available or create one). zoom will get you there if the same is visible in the zoom window.


what is zoom window ?
zoom window is size of the zoom glass represented by
-1,0
-1 - no of lines to check before the exec line.
0 - no of lines to check after the exec line.


you could set the zoom window by
set zw -1,0 or 'zwindow' => '-1,0' in preferences



I would rather use breakpoint instead of all these?


True. if you know how to handle these with breakpoints, it'd be the better idea. but i have highlighted simple example to illustrate the idea. Think about a scenario where it is extremely complex, and the process is forked... evaled... many times, and finally a small piece of code is executed which you want to debug. Defining breakpoints will be cumbersome here Instead, tell zoom where ``this pattern occurs'', or zoom where ``bookmark'' is visible. In many cases, setting a bookmark with your name ex.# KARTHI will make sense for the context. By hitting zoom you will reach right there.






Do you want to set your own boundaries for debugging ?


Ans. Boundary


Debugging is a recursive process. Often times, you'll have a large code base where you have used your modules and also CPAN modules. So, how can you restrict yourself to debugging your modules alone? It should be as simple as skipping module code if the filename contains 'lib/perl5' (considered CPAN). This sets boundaries in debugging, so that debugging becomes easier. If it is easier for you to define postive conditions use 'show', and for negative conditions use 'skip'.



Configuration



'boundary' => { # Boundary
'show' => { # Show
'files' => [ '', ], # bs.1
'packs' => [ '', ], # bs.2
'subs' => [ '', ], # bs.3
},
'skip' => { # Skip
'files' => [ '', ], # bs.4
'packs' => [ '', ], # bs.5
'subs' => [ '', ], # bs.6
},
}



# bs.1 boundary - show all matching files
# bs.2 boundary - show all matching packages
# bs.3 boundary - show all matching subs
# bs.4 boundary - skip all matching files
# bs.5 boundary - skip all matching packages
# bs.6 boundary - skip all matching subs





Want more choices in debugging forked process ?


Ans. Custom forktty mode


Debugging a forked process is always a challenge. There are already some solutions using the xterm method, where the parent spawns xterms and associates child process with a new tty. But, not everyone uses xterm for debugging. Most people ssh/telnet to work from a remote location and often don't use X windows or xterm applications. sdb can provide a way of debugging the forked process without xterm.



Configuration



'forktty' => {
'mode' => 0,
'tmpdir' => "$ENV{HOME}/.sdb/fork/",
},


mode = 0 (default), 1 (custom)
tmpdir - custom fork mode requires tmpdir


mode 0 - default behaviour as perl debugger (xterm, os2)



mode = 1 ?

It's more likely that you want to debug a forked process which has more than one child. If you are not comforatble with xterm or it is not available, try to accomplish this with any kind of tty. The trick here is that the parent doesn't create the ttys, and knows about only some file locations. The user creates tty whichever way he wants, and maps each session to a child. Its like going into a session and saying I'm child 1, give the control to me for child 1. This is the same way for the rest of ttys. Though, it's not as simple as xterm where you don't have worry about the creation. It something you can use it, when you left with no options.



how to setup?
a. create sessions (xterm, ssh, telnet, screen, ...)
create sessions whicever way you want (e.x 3 sessions)
b. go the all child session and run script sdbchild.sh
session 1 (run script sdbchild.sh 1)
waiting for child 1 (sleeping)


session 2 (run script sdbchild.sh 2)
waiting for child 2 (sleeping)


session 3 (run script sdbchild.sh 3)
waiting for child 3 (sleeping)



c. run the parent with sdb enabled.


script to map the child tty with dummy files

sdbchild.sh :
ln -sf `tty` $HOME/.sdb/fork/tty$1
echo "waiting for child $1 (sleeping)";
sleep 100000;

Example: parent.pl



package main;
foreach $child (1..3) {
my $pid = fork;
if( defined($pid) && ($pid == 0) ) {
print "i am child $child \n";
foreach $index (1..10) {
print "i am child $child --- $index \n";
}
exit;
} elsif($pid) {
print "i am parent forked $pid \n";
next;
} elsif( $! =~ m/No\s+more\s+process/ ) {
sleep 2;
next;
} else {
warn "fork() failed";
return undef;
}
}


Running the parent:



setenv SDB_PREF "$HOME/.sdb/pref.conf"
perl -d:sdb parent.pl

once forked, you will see the child running the child session.


Understandably, this is not as easy as xterm, but the feature is available if you need it.


There is also a hidden mode. When mode is set to 0 and doesn't find the xterm tty or os2 tty it goes into single mode. This means that the child shares the same tty as the parent. Output gets overlapped. But, when the single child process is forked and the parent dies after forking, you will find this useful, since you don't want to deal with one more tty. There is no way to enable this functionality explictly.





Screenshot - Before Fork



Screenshot - After Fork





So, you want your debugger look pretty?


Ans. Syntax Highlight


There is the option to enable syntax highlighting in the sdb debugger. All you have to do is setup your own colorizer module and enable the module syntax in preferences. Once properly setup, you will see the debugger output as highlighted text. This gives an editor kind of feel and is colorful... code and data are easily decipherable.




Configuration


'syntax' => {
'mode' => 0,
'tmpdir' => "$ENV{HOME}/.sdb/syntax/",
'command' => 'cat %f | perl -I ~ ~/viewperl.pl'
},



mode = 0 (no highlight), 1 (highlight)
tmpdir - syntax highlight requires tmpdir
command - syntax highlight command, %f for internal.

If you have some problems in a solaris version, in normal mode where data is not rendering, set the below and try


'syntax' => {
'mode' => 1,
'tmpdir' => "$ENV{HOME}/.sdb/syntax/",
'command' => 'cat %f',
},


you can get the syntax highlight working by setting up the colorizer. For Perl, there is module already which you can use.



Syntax::Highlight::Perl::Improved

Install this module (there is no dependency, straight forward), even if you are not allowed to install in the system, have only Improved.pm in your path. The package comes with viewperl.pl which uses Improved.pm to generated the highlighted output. Set this up correctly and ensure the same is working on a sample perl code, then you can plug it into sdb as below.



'command' => 'cat %f | perl -I ~ ~/viewperl.pl'
# %f will be replaced by sdb internally by the temp files.

Note: syntax highlighting takes 1 or 2 seconds to render. But, the nonsyntax mode is faster. If anybody knows how to improve the Improved.pm :) it will be much helpful.





Screenshot





Don't want to do everything inside the debugger?


Ans. Debug preferences (SDB_PREF)


Often, you'll know what you want to debug even before you start debugging, and also what your preferences are. Debug preferences help you define the same outside the of the debugger and get initialised when the debugger starts. Some of the features can be overridden during run time. (Note: SDB uses the default preferences defined inside sdb.pm, where there is no SDB_PREF defined. This is good most of the time. If you want set your own preferences and maintain them separately, you should use SDB_PREF)



Configuration



environ
SDB_PREF "$HOME/.sdb/pref.conf"


preferences (static)
$config = {
'code' => { # Code
'window' => '-8,20', # c.1
'zwindow' => '-1,0', # c.2
'caller' => '0', # c.3
'comments' => '1', # c.4
'warnings' => '1', # c.5
'padcode' => '1', # c.6
'quickexit'=> '1', # c.7
},
'data' => { # Data
'render' => '1', # d.1
'subexp' => '1', # d.2
'mode' => '10', # d.3
'skip' => [ '', ], # d.4
},



'breakpt' => { # Breakpoints
'b' => [ '', ], # b.1
'b_load' => [ '', ], # b.2
'b_compile' => [ '', ], # b.3
'b_postpone'=> [ '', ], # b.4
},
'boundary' => { # Boundary
'show' => { # Show
'files' => [ '', ], # bs.1
'packs' => [ '', ], # bs.2
'subs' => [ '', ], # bs.3
},
'skip' => { # Skip
'files' => [ '', ], # bs.3
'packs' => [ '', ], # bs.4
'subs' => [ '', ], # bs.5
},
},
'forktty' => { # Fork
'mode' => 0, # f.1
'tmpdir' => "$ENV{HOME}/.sdb/fork/", # f.2
},
'syntax' => { # Syntax
'mode' => 0, # s.1
'tmpdir' => "$ENV{HOME}/.sdb/syntax/", # s.2
'command' => 'cat %f | perl -I ~ ~/viewperl.pl'
},
};



# c.1 code window size -8 (pre), 20 (post)
# c.2 zoom window size -1 (pre), 0 (post)
# c.3 boolean to show/skip caller segment
# c.4 boolean to show/skip comments in code segment
# c.5 boolean to show/skip warnings while debug
# c.6 boolean to show/skip file,sub,line no while debug
# c.7 boolean to show/skip cleanup while debug
# d.1 boolean to render data segment
# d.2 boolean to render sub expressions
# d.3 flag to set dumper mode and depth
# d.4 variables list to skip in data segment
# b.1 sdb style flexible breakpoints for subroutine
# b.2 sdb style flexible breakpoints for "b load"
# b.3 sdb style flexible breakpoints for "b compile"
# b.4 sdb style flexible breakpoints for "b postpone"
# bs.1 boundary - show all matching files
# bs.2 boundary - show all matching packages
# bs.3 boundary - show all matching subs
# bs.4 boundary - skip all matching files
# bs.5 boundary - skip all matching packages
# bs.6 boundary - skip all matching subs
# f.1 forktty mode 0 - default, 1 (custom)
# f.2 tmpdir - used by the custom mode
# s.1 syntax highlight 0 - default, 1 (higlight)
# s.2 tmpdir - used for syntax highlight
# s.3 command - colorizer command to run






FAQ - Debugger Unleashed






Lets talk problems...


It has become very common that we build things on top of some existing system. Nobody wants to reinvent the wheel or start from scratch. So we often copy/paste the code to modify or build a new system. Not only that, people involved in maintaining the application are relatively high-level compared to whoever developed it. So very often we have to enhance the exsiting system or support the same. So it becomes an inherent necessity to understand the existing code.


lets try to explore some common techniques that we can use to understand the code



a. comments in the code (describing the logic)
b. logs (data elements captured in runtime)
c. test cases and etc.




What are comments ?


Comments are a piece of natural language (mostly english) to describe the logic implemented in programming language. So what is good about comments? Comments are really helpful for understanding code, as often comments can be used for high level description of the logic. Then, what's bad? If we have to comment every single line, the code will be untidy and often doesn't serve its purpose. Point being: just because it's in english, doesn't mean that we can understand what it means. Why you cannot understand english? Just as everybody has their own style of programming, they have their style of commenting too. You can find a 20 line comment for 5 lines of code or a two line comment for 30 lines of code. sometimes both make sense, but many times not. I know people who write very elegant code, but don't comment at all. There could be multiple reasons for the same (like there is no thrill in writing comments, lack of time or purposely not commenting (job security)) or very little comments about the logic, most of the time the comments will be short and cryptic for the intial developer's reference. Not surprisingly, many people don't trust comments and trust only the code because thats the thing which works :)




What's logging?


Logging is a method of capturing the runtime data into a file for later debugging/troubleshooting. Logs are often useful for understanding where exactly some problem occured(which part of the program), and the supporting details to understand why the problem occured. Often, people confuse debug logs with error logs. Error logs are a permanent part of the program which are used to log details for later debugging. Debug logs are a temporary part of the program which get introduced when error logs don't help in understanding/solving the problem. Debug logs often become a permanent part of program if we don't have proper method to disable or remove them. That's why logs become huge and often unhelpful because of so many details. Debugging via log comments is an iterative process. Some people begin debugging near the problem or go one level up and repeat the process until the problem is identified. Others start from the top and traverse the entire process until the problem is identified. In both cases, debug logs get introduced at every level during iterative runs of a program to generate logs for problem identification.




What's code?


This is the real problem creator. Sometimes, we understand code poorly. Everybody has their own creativity and level of understanding, so there are many ways of programming things and many coding styles. When we try understand others' code, we often take the help of program logs and debug print statements and the power of our IMAGINATION. Imagination is sometimes born with an evil brother ASSUMPTION, which gets us into a lot of trouble. :) Coding standards can help, how often do they really succeeded?





then, how do we solve the problem of understanding others' code?


Solving a problem via the above can be exponential because it involves individual responsibility. Sometimes the solution to understanding language design is using the debugger.




So, what's debugger?


This provides a platform for you to find bugs and understand code flow when above options don't help you very much. Debuggers are often very interactive: if you talk to it, it talks back. If you want something, the debugger finds it and gives it for you. Unfortunately this works similarly as most languages.






Lets talk solutions...





Smart debugger?


Smart debugger understands the very purpose of debugging and most common needs. So, it provides details proactively, which helps you to understand code flow quickly. Often, you may not know how a simple thing is being implemented if it is done in a very complex way. Smart debugger was created with those situations in mind.




What are smart debugger features?


higlevel



a. data rendering.
b. code window/markers
c. flexible breakpoints.
d. zoom points for bookmarks/line regex.
e. debugging boundaries.
f. custom mode for debugging forked process.
g. syntax highlight
h. debug preferences for instant debugging.

lowlevel




a. code window size.
b. display comments.
c. display caller.
d. padding the code with program, line no
e. boundary show logic
f. boundary skip logic





Smart Debugger, Not a Magic Debugger?


Smart Debugger is proactive enough to provide necessary details on the fly. However, do not expect it to always work on one liners or obfuscated code. Don't forget that a small percentage of discipline is in the coding. The Smart debugger can be used for mod_perl applications, but I'm not sure about the embedded perl applications...






PERL5DB COMMANDS


SDB is built on top of perl default debugger. So the perl debugger commands also available. To distinguish easily the sdb commands, all the commands starts with ``set'' except ``list'' and ``z''.







KNOWN BUGS



a. lexical parse of very complex variable expressions can fail.
b. unable to differentiate modulus (%) operator with hash (%) operator.
c. no support for glob data type in data rendering.
d. in some cases, ctrl-c only exits debugger.
e. debugger inside emacs is not supported.





DEPENDENCY


Data::Dumper, IO::Handle







VERSION


Version 0.01






CONCEPT


Smart Debugging is a concept more than just a tool in perl. I implemented this first in perl, since perl is closer to my heart. this applies for any scripting/programming languages including C, Python, Ruby ..






SEE ALSO



perldebug(1), Devel::ptkdb






AUTHOR


Karthikeyan Rajaram karthi_ir@yahoo.com


Copyright (c) 2007-08 Karthikeyan Rajaram. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.