Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
UppHub
Status & Roadmap
FAQ
Authors & License
Forums
Funding U++
Search on this site











SourceForge.net Logo

SourceForge.net Logo

GitHub Logo

Discord Logo

Debugger pretty printing scripts


Table of Contents

 

1 Introduction

2 Script inputs

3 Data and type manipulation functions

4 Output functions

5 Debugger helper function to create script skeleton

6 Examples

 


Introduction

 

This feature is currently only available in Windows PDB debugger.

 

TheIDE PDB debugger can use scripts, stored in .dbg files in packages, to display user defined types nicely in the debugger. Scripts from .dbg files are loaded at the start of debugging.

 

Scripts are based on Esc scripting language.

 

Scripts can contain two kinds top-level elements

 

fn function(params) { .. }

 

defines Esc function.

 

typename type { .. }

 

defines script for pretty printing of type.

 


Script inputs

 

Script receives following implicit input parameters:

 

value

Map of "address" and "type" numbers, representing the value to print.

template_param

If the type is template, array of template parameters (types). These usually go right away to ITEM_TYPE.

from

If there are multiple values (ITEM_COUNT was used), first value to return (e.g. with ITEM_PTR)

items

If there are multiple values (ITEM_COUNT was used), number of values to return.

 


Data and type manipulation functions

 

SizeOf(x)

Returns the size of type. Parameter can be string, type number or map with "type" number attribute

TypeName(x)

Returns the name of type. Parameter can be type number or map with "type" number attribute.

TypeNo(s)

Convertes type string to type number.

PeekPtr(x)

Reads a pointer value from debugee. Parameter is either number or map with "address".

Peek8(x)

Reads a byte from debugee. Parameter is either number or map with "address".

Peek16(x)

Reads 16-bit integer from debugee. Parameter is either number or map with "address".

Peek32(x)

Reads 32-bit integer from debugee. Parameter is either number or map with "address".

Peek64(x)

Reads 64-bit integer from debugee. Parameter is either number or map with "address".

Peek32f(x)

Reads 32-bit FP number (float) from debugee. Parameter is either number or map with "address".

Peek64f(x)

Reads 64-bit FP number (double) from debugee. Parameter is either number or map with "address".

SizeOfPtr()

Returns the size of pointer (in bytes) in debugee. 4 for 32-bit mode, 8 for 64-bit.

NestedType(t, id)

Returns type number of nested type id. t can be type number or map with "type".

DeRef(x)

Parameter must be map with "address". Returns the same with "address" replaced with pointer value read from debugee. Similar to x.address = PeekPtr(x.address).

Field(x, id)

Parameter must be map with "address" and "type". id should be the name of member variable of "type". Returns new map with "address" and "type" corresponding to the given member variable.

Align(address, size)

Adjusts address for proper standard alignment for element with size.

 


Output functions

Output function define how debugger displays the variable.

TEXT(x, color = 1)

Adds text output to pretty printing. x can be string or number. color is specified by number from the following set:

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ITEM_COUNT(x)

Reports the number of items in the container. If not called, the value is displayed as "single" (no number of elements and no list of elements). However, even single value can contain ITEM_TYPE and ITEM_PTR, which designate single value to be displayed in debugger.

ITEM_TYPE(x)

Returns the type of value whose address is returned with one of ITEM_PTR variants. Can be called multiple times (debugger then displays tuples - useful for maps).

ITEM_PTR(adr)

Adds a value address to be displayed in the debugger. Printing script should call this exactly items times for each ITEM_TYPE. As Esc scripting language is not exactly fast, it is better to use ITEM_PTRS or ITEM_DEREF_PTRS instead.

ITEM_PTRS(adr, sz, from, items)

Adds multiple value addresses - much faster variant of script code

for(i = 0; i < items; i++)

    ITEM_PTR(address + (i + from) * sz)

ITEM_DEREF_PTRS(adr, from, items)

Adds multiple value addresses by derefencing pointers- much faster variant of script code

sz = SizeOfPtr();

for(i = 0; i < items; i++)

    ITEM_PTR(PeekPtr(address + (i + from) * sz));

ITEM_DEREF_PTRSZ(adr, sz, from, items)

Adds multiple value addresses by derefencing pointers- much faster variant of script code

for(i = 0; i < items; i++)

    ITEM_PTR(PeekPtr(address + (i + from) * sz));

STRING()

Signals to debugger that value should be displayed as string. ITEM_TYPE must be some kind of integer type that is then interpreted as UNICODE characters.

CHUNK(x)

Debugger normally tries to load and display value items by 10000 chunks, which might be hard for scripts to manage at reasonable speed and with set processing steps limit, especially if using ITEM_PTR function. CHUNK can reduce this number to something more manageable.

 


Debugger helper function to create script skeleton

 

There is a special menu entry that helps with creating pretty printing scripts:

 

 


Examples

Examples are in reference/DbgScripts:

Example C++ type

Debbuger pretty printing script

class MyString {

   int len;

   union {

       char  *ptr;

       char   data[16];

   };

 

public:

   MyString(const char *s) {

       len = strlen(s);

       if(len < 16)

           memcpy(data, s, len + 1);

       else {

           ptr = new char[len + 1];

           memcpy(ptr, s, len + 1);

       }

   }

 

   ~MyString() {

       if(len >= 16)

           delete []ptr;

   }

};

typename MyString {

   length = Peek32(Field(value, "len"));

   STRING();

   ITEM_COUNT(length);

   ITEM_TYPE("char");

   if(length < 16)

       ITEM_PTRS(Field(value, "data").address, 1, from, items);

   else

       ITEM_PTRS(DeRef(Field(value, "ptr")).address, 1, from, items);

}

template <class T>

class MyArray {

   T *begin;

   T *end;

   

public:

   MyArray(int n) { begin = new T[n]; end = begin + n; }

   ~MyArray()     { delete begin; }

   

   T& operator[](int i) { return begin[i]; }

};

typename MyArray {

   begin = DeRef(Field(value, "begin"));

   end = DeRef(Field(value, "end"));

   sz = SizeOf(begin.type);

 

   ITEM_COUNT((end.address - begin.address) / sz);

   ITEM_TYPE(template_param[0]);

   ITEM_PTRS(begin.address, sz, from, items);

}

template <class T>

class MyIndirectArray {

   int          n;

   MyArray<T *> a;

public:

   T& operator[](int i) { return *a[i]; }

 

   MyIndirectArray(int n) : a(n), n(n) { for(int i = 0; i < n; i++) { a[i] = new T; } }

   ~MyIndirectArray() { for(int i = 0; i < n; i++) { delete a[i]; } }

};

typename MyIndirectArray {

   a = Field(value, "a");

   begin = DeRef(Field(a, "begin"));

   end = DeRef(Field(a, "end"));

   ITEM_COUNT((end.address - begin.address) / SizeOfPtr());

   ITEM_TYPE(template_param[0]);

   ITEM_DEREF_PTRS(begin.address, from, items);

}

template <class K, class V>

class MyMap {

   MyArray<K> keys;

   MyArray<V> values;

   

public:

   MyMap(int n) : keys(n), values(n) {}

   

   void Set(int i, K key, V value) { keys[i] = key; values[i] = value; }

};

typename MyMap {

   k = Field(value, "keys");

   begin = DeRef(Field(k, "begin"));

   end = DeRef(Field(k, "end"));

   sz = SizeOf(begin.type);

   ITEM_COUNT((end.address - begin.address) / sz);

   ITEM_TYPE(template_param[0]);

   ITEM_PTRS(begin.address, sz, from, items);

 

   v = Field(value, "values");

   begin = DeRef(Field(v, "begin"));

   end = DeRef(Field(v, "end"));

   sz = SizeOf(begin.type);

   ITEM_COUNT((end.address - begin.address) / sz);

   ITEM_TYPE(template_param[1]);

   ITEM_PTRS(begin.address, sz, from, items);

}

 

Do you want to contribute?