Powerbuilder .DBG files (using /PBDEBUG)

Did you already try to produce a .dbg file by running your powerbuilder application with the /DEBUG or /PBDEBUG parameter ? It is a trace of each call and executed line, so it could help to find where an application crash in production env, but is is slow to produce (huge file).

This is resuling in an utf16-le encoded text file, indented according to the callstack (posted functions are at first level)

But I did found any tools to view them as a tree representation, so I build a perl script with a tk front-end. This is very simple but it as a feature allowing to build a tree even when the file is truncated; I mean that it doesn’t begin at first level in callstack. You could see “rescue” keyword on the console during loading of the file.

Extract of a dbg file : produce by running @dwdebugmachine105.exe /DEBUG@

Executing object function +CREATE for class DWDEBUGMACHINE105, lib entry DWDEBUGMACHINE105
  Executing instruction at line 2
  Executing instruction at line 3
  Executing object function +CREATE for class MESSAGE, lib entry _TYPEDEF
  End object function +CREATE for class MESSAGE, lib entry _TYPEDEF
  Executing instruction at line 4
  Executing object function +CREATE for class TRANSACTION, lib entry _TYPEDEF
  End object function +CREATE for class TRANSACTION, lib entry _TYPEDEF
  Executing instruction at line 5
  Executing object function +CREATE for class DYNAMICDESCRIPTIONAREA, lib entry _TYPEDEF
  End object function +CREATE for class DYNAMICDESCRIPTIONAREA, lib entry _TYPEDEF
  Executing instruction at line 6
  Executing object function +CREATE for class DYNAMICSTAGINGAREA, lib entry _TYPEDEF
  End object function +CREATE for class DYNAMICSTAGINGAREA, lib entry _TYPEDEF
  Executing instruction at line 7
  Executing object function +CREATE for class ERROR, lib entry _TYPEDEF
  End object function +CREATE for class ERROR, lib entry _TYPEDEF
  Executing instruction at line 8
End class function +CREATE for class DWDEBUGMACHINE105, lib entry DWDEBUGMACHINE105
Executing event +OPEN for class DWDEBUGMACHINE105, lib entry DWDEBUGMACHINE105
  Executing instruction at line 1
  Executing class function OPEN for class SYSTEMFUNCTIONS, lib entry _TYPEDEF
    Executing system dll function
    Executing object function +CREATE for class W_MAIN, lib entry W_MAIN
      Executing instruction at line 2
      Executing object function +CREATE for class CB_PAL, lib entry W_MAIN
      End object function +CREATE for class CB_PAL, lib entry W_MAIN
      Executing instruction at line 3
      Executing object function +CREATE for class CB_DEBUG, lib entry W_MAIN
      End object function +CREATE for class CB_DEBUG, lib entry W_MAIN
      Executing instruction at line 4
...

And a screen shoot of the dbg-treeviewer running by @perl pbdbgview.pl dwdebugmachine105.dbg@:

Powerbuilder DBG file viewer

For those who don’t have|want a Perl installation, I provide an all-in-one file.

The Perl source is:

#!/usr/bin/perl
#A powerbuilder dbg file (Generated by calling a PB executable with the /DEBUG option)
require 5.004;
use Tk 800.000;
use Tk::widgets;
use Tk::Tree;
#~ use Tk::Adjuster;
use Fcntl qw( SEEK_SET SEEK_CUR SEEK_END );
sub systell { sysseek($_[0], 0, SEEK_CUR) }
our $VERSION = 0.01;
$|++;  #autoflush
if(@ARGV != 1){
  print "Usage: $0 your.dbg";
  exit(255);
}
print "$0 v${VERSION} - (c) Nicolas Sébastien Pierre-Olivier GEORGES\n";
print "A powerbuilder dbg file viewer (can be generated by calling a PB executable with the /DEBUG option)\n\n";
my $file = shift @ARGV;
my $title = "Powerbuilder DBG Viewer - $file";
my $geometry = {
  width  => 1000,
  height => 400,
  posx   => 31,
  posy   => 0
};
# MainWindow
my $mw = MainWindow->new(-relief => 'flat', -borderwidth => 3);
$mw->geometry("$geometry->{width}x$geometry->{height}+$geometry->{posx}+$geometry->{posy}");
$mw->title("$title");
# Button Frame and Button
my $buttonFrame = $mw->Frame(-height => '12', -relief => 'flat', -borderwidth => 0)->pack(-side => 'top', -fill => 'x', -anchor => 'n');
$buttonFrame->Label(
  -textvariable => \$title,
  -font        => "{verdana} 8 {bold}",
  -foreground => 'RoyalBlue3',
  -background => 'grey86',
  -relief => 'flat',
  -borderwidth => 1,
  -anchor => 'w'
)->pack(-side => 'left', -fill => 'both', -expand => 1);
my $exitbutton  = $buttonFrame->Button(
  -text => 'Close',
  -font        => "{verdana} 8 {normal}",
  -background => 'grey86',
  -foreground => 'RoyalBlue3',
  -relief => 'raised',
  -borderwidth => 1,
  -command => sub {exit}
)->pack(-side => 'right');
$exitbutton->bind('<Enter>' => sub {$_[0]->configure(-activebackground => 'grey86',  -activeforeground => 'red', -relief => 'raised')});
$exitbutton->bind('<Leave>' => sub {$_[0]->configure(-activebackground => 'grey86',  -activeforeground => 'RoyalBlue3', -relief => 'raised')});
# Work area Frame
$workareaFrame = $mw->Frame(-relief => 'groove', -borderwidth => 1)->pack(-side => 'top', -fill => 'both', -expand => 1, -anchor => 'n');
# Tk::Tree Widget
$tree = $workareaFrame->Scrolled(
    'Tree',
    -background         => 'white',
    -selectmode         => 'extended',
    -selectbackground   => 'LightGoldenrodYellow',
    -selectforeground   => 'RoyalBlue3',
    -highlightthickness => 0,
    -font               => 'verdana 8',
    -relief             => 'flat',
    -scrollbars         => 'osoe',
    -borderwidth        => 0,
    #~ -browsecmd          => \&browse_cmd ,
    #~ -opencmd            => \&open_cmd,
    #~ -closecmd           => \&close_cmd,
   )->pack(-side => 'left', -fill => 'both', -expand => 1, -anchor => 'w');
# Tk::Tree Widget additional configurations
$tree->configure(
    -separator  => '/',
    -drawbranch => 'true',
    -indicator  => 'true',
    -selectborderwidth => '0',
    -selectmode        => 'extended',
    -highlightcolor => 'red',
  );
$tree->focus();
# Tk::Tree Widget window size adjuster
#~ $workareaFrame->Adjuster()->packAfter($tree, -side => 'left');
sub rescue{
  my $head = shift;
  local $prev_id = '';
  print "\n*** RESCUE:\n";
  foreach $id ( 0..($head/2)-1 ){
    $prev_id .=  2*$id . '-'.$.;
    print "***\tcreating node $prev_id\n";
    $tree->add( $prev_id, -text => 'Rescued node', -itemtype => 'text');
    $tree->setmode($prev_id,'close');
    $prev_id.='/';
  }
  chop $prev_id;
  return $prev_id;
}
open( FILE, '<:encoding(UTF-16LE)', $file) or die "Could not open file '$file', $!\n";
sysseek FILE, 0,  SEEK_END;
my $length = systell(FILE);
my ($read,$prc,$lastprc);
seek FILE, 0,  SEEK_SET;
my ($id,$parent,$prev,@parents);
$prev = 0;
$prev_id = '0-1';
$parent = "";
while(<FILE>){
  $read = systell(FILE);
  $prc=int( 100*$read/$length );
  unless($lastprc == $prc){
    print "\rLoading file $prc% ";
    $lastprc = $prc;
  }
  chomp;
  /^(\s*)(.*)$/;
  my $head = length $1;
  my $line = $2;
  $line =~ s/^Executing (instruction at|system|(class|object) function)?//;  
  $line =~ s/, lib.*$//;
  $line =~ s/(\+?\w+?) for class (\w+)/$2.$1/;
  $line =~ s/\+/event /;
  if($prev == $head){
    $id = $parent.$head.'-'.$.;
  }
  elsif($prev < $head){#first of this level
    unless($tree->info('exists', $prev_id)){
      #We are starting a block without parents.
      #auto insert parent
      $prev_id = rescue($head);
    }
    $tree->setmode($prev_id,'close');
    $parent = $prev_id.'/';
    $id = $parent.$head.'-'.$.;
  }
  else{  #return from child level
    $parent =~ /(\d+)(-\d+)?\/$/;
    #~ print "Return from $prev to $1 (head=$head)\n";
    $prev = $1 or 0;
    $parent =~ s/[^\/]+\/$//;
  }
  #~ print "id=$id\tprev=$prev\tprev_id=$prev_id\thead=$head\tparent=`$parent`\n";    
  next if $line =~ /^End /;  #cancel closing tags  
  if($tree->info('exists', $id)){
    #~ print "EXISTS id=$id\tprev=$prev\tprev_id=$prev_id\thead=$head\tparent=`$parent`\n";  
    next;
  }
  #~ print "id=$id\tprev=$prev\tprev_id=$prev_id\thead=$head\tparent=`$parent`\n";  
  $tree->add( $id, -text => $line, -itemtype => 'text');
  $prev=$head;
  $prev_id = $id;
}
close FILE;
MainLoop;

Comments and bugs reports are welcome 

Attachments