QTools  6.8.1
QSPY Dictionaries

About QSPY Dictionaries

By the time you compile and load your application image to the Target, the symbolic names of various objects, function names, and event signal names are stripped from the code. Therefore, if you want to have the symbolic information available to the QSPY host-resident component, you need to supply it somehow to the software tracing system.

The QS Target-resident component provides special dictionary trace records designed expressly for providing the symbolic information about the target code in the trace itself. These "dictionary records" are very much like the symbolic information embedded in the object files for the traditional single-step debugger. QS can supply four types of dictionary trace records:

  • object dictionary – for identifying objects (HSMs, AOs, Time events, etc)
  • function dictionary – for identifying functions (state-handlers)
  • signal dictionary – for identifying event signals
  • user dictionary – for identifying application-specific (user) records
QSPY does not absolutely require the presence of dictionary trace records to generate the human-readable output, in the same way as the symbolic information in the object files is not absolutely required to debug code. However, in both cases, the availability of the symbolic information greatly improves the readability of the disassembly code shown in the debugger.

For instance, the following listing shows an example of the QSPY text output when the "dictionaries" are not available. (NOTE: dictionaries might not be available because (1) the Target does not produce dictionary trace records for all relevant objects or (2) the Target does produce all dictionaries, but QSPY has never seen them, because it was attached to the Target after the dictionaries have been transmitted):

. . . . . .
0009692721 Disp===> Obj=0x2000133C,Sig=00000010,Obj=0x2000133C,State=0x000010C5
0009693389 USER+000 4 hungry
0009695383 USER+000 4 eating
0009695932 =>Intern Obj=0x2000133C,Sig=00000010,Obj=0x2000133C,State=0x000010C5
===RTC===> St-Entry Obj=0x200012F8,State=0x00000D49
0009697188 ===>Tran Obj=0x200012F8,Sig=00000011,Obj=0x200012F8,State=0x00000DD5->0x00000D49
0009698052 Disp===> Obj=0x200012F8,Sig=00000004,Obj=0x200012F8,State=0x00000D49
===RTC===> St-Entry Obj=0x200012F8,State=0x00000CA9
0009699697 ===>Tran Obj=0x200012F8,Sig=00000004,Obj=0x200012F8,State=0x00000D49->0x00000CA9
0009700602 Disp===> Obj=0x200012B8,Sig=00000004,Obj=0x200012B8,State=0x00000DD5
0009701247 =>Intern Obj=0x200012B8,Sig=00000004,Obj=0x200012B8,State=0x00000DD5
0009702038 Disp===> Obj=0x20001278,Sig=00000004,Obj=0x20001278,State=0x00000DD5
0009702677 =>Intern Obj=0x20001278,Sig=00000004,Obj=0x20001278,State=0x00000DD5
0009703468 Disp===> Obj=0x20001238,Sig=00000004,Obj=0x20001238,State=0x00000DD5
0009704107 =>Intern Obj=0x20001238,Sig=00000004,Obj=0x20001238,State=0x00000DD5
0009704899 Disp===> Obj=0x200011F8,Sig=00000004,Obj=0x200011F8,State=0x00000DD5
0009705538 =>Intern Obj=0x200011F8,Sig=00000004,Obj=0x200011F8,State=0x00000DD5
0015961105 Disp===> Obj=0x200011F8,Sig=00000011,Obj=0x200011F8,State=0x00000DD5
===RTC===> St-Exit  Obj=0x200011F8,State=0x00000DD5
0015962823 Disp===> Obj=0x2000133C,Sig=00000010,Obj=0x2000133C,State=0x000010C5
0015963491 USER+000 0 hungry
. . . . . .

And here is the exact same trace data when the "dictionaries" are available:

. . . . . .
0009692721 Disp===> Obj=l_table,Sig=HUNGRY_SIG,State=Table_serving
0009693389 PHILO_STAT 4 hungry
0009695383 PHILO_STAT 4 eating
0009695932 =>Intern Obj=l_table,Sig=HUNGRY_SIG,State=Table_serving
===RTC===> St-Entry Obj=l_philo<4>,State=Philo_hungry
0009697188 ===>Tran Obj=l_philo<4>,Sig=TIMEOUT_SIG,State=Philo_thinking->Philo_hungry
0009698052 Disp===> Obj=l_philo<4>,Sig=EAT_SIG,State=Philo_hungry
===RTC===> St-Entry Obj=l_philo<4>,State=Philo_eating
0009699697 ===>Tran Obj=l_philo<4>,Sig=EAT_SIG,State=Philo_hungry->Philo_eating
0009700602 Disp===> Obj=l_philo<3>,Sig=EAT_SIG,State=Philo_thinking
0009701247 =>Intern Obj=l_philo<3>,Sig=EAT_SIG,State=Philo_thinking
0009702038 Disp===> Obj=l_philo<2>,Sig=EAT_SIG,State=Philo_thinking
0009702677 =>Intern Obj=l_philo<2>,Sig=EAT_SIG,State=Philo_thinking
0009703468 Disp===> Obj=l_philo<1>,Sig=EAT_SIG,State=Philo_thinking
0009704107 =>Intern Obj=l_philo<1>,Sig=EAT_SIG,State=Philo_thinking
0009704899 Disp===> Obj=l_philo<0>,Sig=EAT_SIG,State=Philo_thinking
0009705538 =>Intern Obj=l_philo<0>,Sig=EAT_SIG,State=Philo_thinking
0015961105 Disp===> Obj=l_philo<0>,Sig=TIMEOUT_SIG,State=Philo_thinking
===RTC===> St-Exit  Obj=l_philo<0>,State=Philo_thinking
0015962823 Disp===> Obj=l_table,Sig=HUNGRY_SIG,State=Table_serving
0015963491 PHILO_STAT 0 hungry
. . . . . .

As you can see, the difference in readability is quite dramatic.

Acquiring Dictionaries

The QS Target-resident component generates the dictionary trace records during the initialization of active objects components in the Target code, that is, typically right after the reset. Consequently, the best way to acquire the dictionaries is to capture the trace when the Target performs the reset. This can be done in a couple of ways:

  • Start QSPY before the Target resets
  • Manually reset the Target while QSPY is running (e.g., press a Reset button on the Target board)
  • Send the RESET command to the Target from QSPY while it is running

Either way, the dictionary records should be produced and acquired by the Target. The following listing shows the dictionary records sent by the DPP example application running on the EFM32-SLSTK3401A board:

C:\qp\qpc\examples\arm-cm\dpp_efm32-slstk3401a\qxk\gnu>qspy -cCOM6
QSPY 6.1.0 Copyright (c) 2005-2018 Quantum Leaps
Documentation: https://www.state-machine.com/qtools/qspy.html
Current timestamp: 180119_195728
-u 7701
-c COM7
-v 600
-T 4
-O 4
-F 4
-S 2
-E 2
-Q 1
-P 2
-B 2
-C 2

########## Trg-RST  QP-Ver=604,Build=180117_155932
           Obj-Dict 0x00007322->QS_RX
           Obj-Dict 0x20000D3C->l_SysTick_Handler
           Obj-Dict 0x20000D3D->l_GPIOPortA_IRQHandler
           Usr-Dict 00000070->PHILO_STAT
           Usr-Dict 00000071->PAUSED_STAT
           Usr-Dict 00000072->COMMAND_STAT
           Obj-Dict 0x200015D0->smlPoolSto
           Obj-Dict 0x20001684->tableQueueSto
           Obj-Dict 0x20001620->philoQueueSto<0>
           Obj-Dict 0x20001634->philoQueueSto<1>
           Obj-Dict 0x20001648->philoQueueSto<2>
           Obj-Dict 0x2000165C->philoQueueSto<3>
           Obj-Dict 0x20001670->philoQueueSto<4>
           Obj-Dict 0x20001AEC->l_philo<0>
           Obj-Dict 0x20001B14->l_philo<0>.m_timeEvt
           Obj-Dict 0x20001B24->l_philo<1>
           Obj-Dict 0x20001B4C->l_philo<1>.m_timeEvt
           Obj-Dict 0x20001B5C->l_philo<2>
           Obj-Dict 0x20001B84->l_philo<2>.m_timeEvt
           Obj-Dict 0x20001B94->l_philo<3>
           Obj-Dict 0x20001BBC->l_philo<3>.m_timeEvt
           Obj-Dict 0x20001BCC->l_philo<4>
           Obj-Dict 0x20001BF4->l_philo<4>.m_timeEvt
           Fun-Dict 0x000026C5->Philo::initial
           Fun-Dict 0x000027E1->Philo::thinking
           Fun-Dict 0x0000287D->Philo::hungry
           Fun-Dict 0x00002925->Philo::eating
           Sig-Dict 00000010,Obj=0x20001AEC->HUNGRY_SIG
           Sig-Dict 00000011,Obj=0x20001AEC->TIMEOUT_SIG
. . .      . . .
           Obj-Dict 0x20001C08->l_table
           Fun-Dict 0x000030CB->QP::QHsm::top
           Fun-Dict 0x00002A79->Table::initial
           Fun-Dict 0x00002B8D->Table::active
           Fun-Dict 0x00002BB9->Table::serving
           Fun-Dict 0x00002E29->Table::paused
           Sig-Dict 00000005,Obj=0x00000000->DONE_SIG
           Sig-Dict 00000004,Obj=0x00000000->EAT_SIG
           Sig-Dict 00000006,Obj=0x00000000->PAUSE_SIG
           Sig-Dict 00000007,Obj=0x00000000->SERVE_SIG
           Sig-Dict 00000008,Obj=0x00000000->TEST_SIG
           Sig-Dict 00000010,Obj=0x20001C08->HUNGRY_SIG
. . .      . . .

Once QSPY acquires the dictionaries, it keeps them in the memory and applies them to display the data in symbolic form (rather than hex addresses).

The dictionaries do not need to be complete to be useful. QSPY simply applies the symbolic information whenever it can find a match in the dictionaries acquired so far. When a dictionary entry is not available, QSPY displays only hex addresses.

Saving Dictionaries to a File

QSPY can save the dictionaries acquired thus far into a file. This must be triggered by the user (by means of the d keyboard command or from QSpyView menu "File->Save Dictionaries"), because QSPY does not "know" when the dictionaries are "complete", therefore it cannot know when to save them automatically.

For dictionaries to be saved to a file, the QSPY host application must be lauched with the -d [file] command-line option, with of without the optional [file] parameter.

On the other hand, QSPY generates automatically the file name for saving dictionaries. This file name has always the form qspy<target-time-stamp>.dic, where <target-time-stamp> unambiguously identifies the Target build date and time. For example, the Target code last built on August 31, 2015 at 14:42:29 will have the name qspy150831_144229.dic.

The internal addresses of objects can change by every code re-build, so dictionaries are applicable only to the specific Target build and must be freshly re-acquired after every new Target code build.

The dictionaries are saved to a file in ASCII format. The following listing shows the dictionaries from the DPP example application running on the EK-TM4C123GXL board:

21 4
0x00007322 QS_RX
0x20000D3C l_SysTick_Handler
0x20000D3D l_GPIOPortA_IRQHandler
0x200015D0 smlPoolSto
0x20001620 philoQueueSto<0>
0x20001634 philoQueueSto<1>
0x20001648 philoQueueSto<2>
0x2000165C philoQueueSto<3>
0x20001670 philoQueueSto<4>
0x20001684 tableQueueSto
0x20001AEC l_philo<0>
0x20001B14 l_philo<0>.m_timeEvt
0x20001B24 l_philo<1>
0x20001B4C l_philo<1>.m_timeEvt
0x20001B5C l_philo<2>
0x20001B84 l_philo<2>.m_timeEvt
0x20001B94 l_philo<3>
0x20001BBC l_philo<3>.m_timeEvt
0x20001BCC l_philo<4>
0x20001BF4 l_philo<4>.m_timeEvt
0x20001C08 l_table
9 4
0x000026C5 Philo::initial
0x000027E1 Philo::thinking
0x0000287D Philo::hungry
0x00002925 Philo::eating
0x00002A79 Table::initial
0x00002B8D Table::active
0x00002BB9 Table::serving
0x00002E29 Table::paused
0x000030CB QP::QHsm::top
3 1
0x00000046 PHILO_STAT
0x00000047 PAUSED_STAT
0x00000048 COMMAND_STAT
16 4
00000004 0x00000000 EAT_SIG
00000005 0x00000000 DONE_SIG
00000006 0x00000000 PAUSE_SIG
00000007 0x00000000 SERVE_SIG
00000008 0x00000000 TEST_SIG
00000010 0x20001B5C HUNGRY_SIG
00000010 0x20001BCC HUNGRY_SIG
00000010 0x20001B94 HUNGRY_SIG
00000010 0x20001AEC HUNGRY_SIG
00000010 0x20001B24 HUNGRY_SIG
00000010 0x20001C08 HUNGRY_SIG
00000011 0x20001B94 TIMEOUT_SIG
00000011 0x20001BCC TIMEOUT_SIG
00000011 0x20001B5C TIMEOUT_SIG
00000011 0x20001AEC TIMEOUT_SIG
00000011 0x20001B24 TIMEOUT_SIG
0 4

Using Dictionary File

The dictionary file saved in previous QSPY sessions can be used in two ways:

  • you can specify the dictionary file in the -d <dictionary_file> command-line option to QSPY. In this case QSPY reads the dictionaries from the provided <dictionary_file> before processing any trace records from the Target. (NOTE: in this case you don't need to provide any of the upper-case command-line options, because they are read from the dictionary file.) For example: command line: qspy -d qspy180117_155932.dic will attempt to read the dictionaries from the specified file.
  • you can specify the dictionary option without the dictionary file -d command-line option to QSPY. Subsequently, once you run QSPY, you can query the Target information (by means of the i or r keyboard command or from QSpyView menu "Commands->Query Target Info"). When the Target replies and provides its build-time-stamp, QSPY looks for the corresponding dictionary file in the current directory and if such a file is found, QSPY reads the dictionaries from it. (NOTE: this option requires that the Target implements the QS receive channel, QS-RX, so that it can receive commands from QSPY).

Next: QSPY UDP Interface