kdbg-2.5.4/ 0000775 0000000 0000000 00000000000 12237506721 0012436 5 ustar 00root root 0000000 0000000 kdbg-2.5.4/BUGS 0000664 0000000 0000000 00000000275 12237506721 0013125 0 ustar 00root root 0000000 0000000 Here is an unsorted list of known deficiencies of kdbg.
- Handles only C and C++
- Cannot handle source file names which contain new-lines or
a sequence of the form
:[0-9]+:[0-9]+:beg
kdbg-2.5.4/CMakeLists.txt 0000664 0000000 0000000 00000000624 12237506721 0015200 0 ustar 00root root 0000000 0000000 cmake_minimum_required(VERSION 2.6)
set(KDBG_VERSION 2.5.4)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kdbg/version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kdbg/version.h)
find_package(KDE4 REQUIRED)
add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
include(KDE4Defaults)
include(MacroLibrary)
#include(ManualStuff.cmake)
#include(ConfigureChecks.cmake)
add_subdirectory(kdbg)
add_subdirectory(po)
kdbg-2.5.4/COPYING 0000664 0000000 0000000 00000035451 12237506721 0013501 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
kdbg-2.5.4/ChangeLog-pre-2.2.0 0000664 0000000 0000000 00000027177 12237506721 0015447 0 ustar 00root root 0000000 0000000 Later versions
Please use the gitweb log at http://repo.or.cz/w/kdbg.git to browse
the changes.
Version 2.0.4
Fixed encoding of the Czech translation thanks to Jakub Galgonek.
Added support for QString in Qt4's debug libraries.
Fixed that the debugger window really comes to the foreground and
receives the focus when the debuggee stops at a breakpoint, when this
option is on (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=171845).
Added a filter edit box to the Attach to Process dialog to improve
usability.
Version 2.0.3
Fixed parsing of gdb output that mentions "operator<<", "operator>>",
"operator<", and "operator>" within text delimited by angle brackets <>.
This fixes a crash when any such function is disassembled and other
misbehaviors.
Fixed parsing stack frames that mention "operator<<" or "operator<".
Thanks to Charles Samuels, who pointed out the problem and provided
an initial fix.
Version 2.0.2
Fixed stack display for functions in an anonymous namespace and
for functions whose names involve template parameter lists (thanks to
Andr矇 W繹bbeking).
Fixed environment list which would add back the entry from the edit box
even if it was just deleted.
Fixed that the Run/Continue button was enabled while the program was
running.
Fixed parsing of NaN (Not a Number) floating point values.
Version 2.0.1
Updated Hungarian translation (thanks to Tamas Szanto).
Worked around gdb 6.3 crashes at "info line main" command (thanks to
Stefan Taferner).
Updated XSLT debugger parser for xsldbg >= 3.4.0 (by Keith Isdale).
Version 2.0.0
References and const types are treated like the base type (thanks to
Shaheed).
Fixed parsing of large arrays with many different values, which were
terminated by "...".
Fixed the kdbg.desktop file: Encoding is UTF-8, install in XDG menu
location.
Fixed PS_COMMAND detection for Solaris' /bin/sh.
Version 1.9.7
Added a new animated button in the toolbar.
Fixed Norwegian translation file names.
Version 1.9.6
"" in register dumps was not parsed correctly.
Fixed that variable popup location was computed incorrectly if tab
width is not 0.
Updated the manual.
Implemented printing of Qt4's QStrings.
Version 1.9.5
Fixed some issues when the items in the environment variable list
are selected.
Added a command line option to attach to a process (thanks to
Matthew Allen for the initial code).
Fixed the "Using host libthread_db" error message properly.
Fixed inappropriate icon sizes.
Version 1.9.4
Updated the build system to the latest auto* tools.
Worked around the problem that gdb reports "Using host libthread_db"
on Fedora Core when it processes the file command.
Version 1.9.3
Improved editing of values; it is now possible to edit variables also
in the watch window.
Version 1.9.2
The previous security fix only protects against accidents, not attacks,
as Matt Zimmerman pointed out. Did it right this time.
Basic editing of values in the local variables window is available.
More refinements are still necessary.
Version 1.9.1
Fixed security flaw regarding the program specific debugger command.
Configurable key bindings.
Version 1.9.0
Program arguments that are file names can be browsed for.
Added XSLT debugging (using xsldbg) by Keith Isdale.
The program counter can be changed via point and click.
Improved register formating by Daniel Kristjansson.
"Orphaned breakpoints", i.e. breakpoints that gdb cannot set
immediately, can be set. This helps debug shared libraries and
dynamically loaded modules.
Version 1.2.10
Fixed the "Using host libthread_db" error message.
Fixed inappropriate icon sizes.
Version 1.2.9
The previous security fix only protects against accidents, not attacks,
as Matt Zimmerman pointed out. Did it right this time.
Version 1.2.8
Fixed security flaw regarding the program specific debugger command.
Version 1.2.7
Fixed parsing of stack frames for recent gdbs.
Support vector registers (thanks to Daniel Thor Kristjansson for
initial code).
Work around bug in some gdbs which inhibits printing of QString values.
Version 1.2.6
Opening the Find dialog no longer toggles a breakpoint.
Make mouse wheel work (again) in source, variables, and watch windows.
When a pointer to a struct is expanded the struct is also expanded.
Improved toolbar and application icons.
Version 1.2.5
Now compiles for KDE 3.
Fixed make install for builddir != srcdir.
Fixed status bar flicker. This gives a nice speed-up by a factor of 4
when the contents of an array of 50 QStrings are displayed!
Version 1.2.4
Now compiles for KDE 3 (Beta1).
Support QString of Qt 3.x.
Improved (and fixed) the display of arrays with repeated values.
Fixed crash when a file is reloaded while disassembled code is
displayed.
Fixed parsing of stack frames involving signal handler invocations.
Version 1.2.3
Fixed invisible toolbar under KDE 2.x (really, this time, I promise).
Fixed crash when no line has the cursor (empty files).
Don't display a blank page when a non-existing file was tried to open.
Version 1.2.2
Fixed a special, but common case where removing a breakpoint didn't
work but add more on the same line instead (thanks to Ron Lerech).
Fixed invisible toolbar under KDE 2.1.2 (thanks to Neil Butterworth).
Fixed compilation for gcc 3.0 (thanks to Ben Burton):
Fixed make install if srcdir != builddir.
Changed encoding of German translations (and also Danish, Italian,
Norwegian, Romanian, Slovak, Swedish) to UTF-8, which fixes message
strings under KDE2 (at least for German - couldn't test the others).
Version 1.2.1
Working directory can be browsed for.
Added context menu to move the selected expression from the local
variables window to the watch window.
Fixed crash when environment variables are removed.
Fixed problems with trailing backslashes in watched expressions.
Fixed compilation on FreeBSD (openpty).
Version 1.2.0
Translations for: Hungarian, Japanese, Norwegian (Nynorsk), Serbian,
Turkish
Updated the User's Manual (English, Russian (thanks, Ilmar!), German).
Version 1.1.7beta1
Improved the program icon; made the installation more KDE2 compliant.
Enabled mouse wheel scrolling at various places.
Version 1.1.6
Added memory display.
Single-stepping by instruction.
Watchpoints. Finally! (On Linux/i386 works best with gdb 5!)
Version 1.1.5
Made Delete key work in the watch window.
Breakpoints can be enabled and disabled in the breakpoint list.
Detach from debugged program on exit (and when new program is debugged).
Added a list of recently opened executables (thanks to
Thomas Sparr ).
Version 1.1.4
Fixed endless loop on shutdown.
Brought in line with KDE 1.91 (KDE 2 beta).
Version 1.1.3
Debugging of multi-threaded programs. Requires a gdb that supports
multi-threaded programs, like gdb 5.
Debugger window pops into the foreground when the program stops.
Made tab width a user-settable option.
Version 1.1.2
Display disassembled code.
Version 1.1.1
Use the KDE system fixed font for the source code window.
By default, do not log communication with gdb.
Added an integrated output window (based on code by Judin Max).
Program specific settings can be set. In particular: the debugger
command (required if you are debugging remote devices), the
terminal emulation needed for the program.
Verison 1.1.0
Use docking windows thanks to Judin Max .
Added a register dump window. Based on code by Judin Max.
Implemented "balloons" (tool tips) that show variable values.
./configure fix for NetBSD thanks to
Berndt Josef Wulf .
There's now a Swedish translation thanks to
琀jan Lindbergh .
Version 1.0.2
Save and restore watched expressions.
More adjustments for the KRASH release.
Show count in QStrings like in normal C strings instead
of repeating the characters.
Use QListView instead of KTabListBox.
Version 1.0.1
Added a hack to set a remote target. Thanks to
Johnny Chan .
Display function arguments. Based on suggestions by Johnny Chan.
KDE 2 fixes.
Support builddir != srcdir.
Version 1.0.0
Brought up-to-date for latest KDE 2.
Version 1.0beta3
Removal of minor misfeatures.
Prepared for KDE 2 and Qt 2 (it's a configure option:
--with-kde-version=2).
Added Russian documentation (thanks to
Ilmar S. Habibulin ) and German documentation.
There is now a Spanish translation thanks to
Manuel Soriano .
Version 1.0beta2
Recognize strings with repeated characters: 'x' .
Fix structs with no (additional) data members and other fixes
for gdb 4.18.
Save window size across sessions.
There is now an Italian translation thanks to
Massimo Morin .
Version 1.0beta1
Fixed non-displaying QString (Qt2) with certain gdb 4.17's (at least
mine here, of SuSE 6.1, had a problem :-)
Fixed cases where gdb commands where executed after debuggee has exited.
Do not execute gdb commands after an interrupt.
Updated some translations. Still most are incomplete. Please help!
There is now a Polish translation thanks to
Jacek Wojdel .
Version 0.3.1
The working directory for the program being debugged can be set
(Execution|Arguments).
There's now a global options dialog in place (File|Global Options).
At the moment the debugger program (which must be gdb, but it could be
an experimental gdb version, for example) and the terminal for program
output can be specified.
Fixed Makefiles to support make DESTDIR=/tmp/foo install (which is
needed by packagers and to create relocatable RPMs).
There's now a Danish translation thanks to
Steen Rabol .
Version 0.3.0
Starting with this version, Qt 1.42 and KDE 1.1 is required.
Ported to Qt 2.0 and KDE post-1.1! KDbg now runs with both
KDE 1.1 (using Qt 1.42) and the latest experimental KDE. You can of
course run one version and debug programs written for the other version.
KDbg can now display Qt 2.0's QString values (which are Unicode
strings)!
Environment variables can be set. Changes become effective the next time
the program being debugged is run.
The breakpoint list has been improved. It disables command buttons at
times when it is not possible to change breakpoints. The icons that
show the breakpoint status are now the same as those in the source
window.
Popup menus (context menus) for frequently used commands have been added
to the source code window (thanks to Tom Nguyen )
There's now a Russian translation thanks to
Ilmar Habibulin .
Internal restructuring. These changes are invisible. They just make
future extensions less cumbersome.
Version 0.2.5
This is the last version that supports Qt 1.33 and KDE 1.0.
There's now a Czech translation thanks to
Martin Spirk .
Recognize and report when gdb dies unexpectedly. This happens commonly
when writing CORBA programs since gdb obviously has problems in
debugging C++ classes with virtual base classes.
Added conditional breakpoints and ignore counts.
Version 0.2.4
Added a toolbar button to load the executable. The button to open a
source file is still there. I hope it's clear which one does what.
Attaching to a running process is now possible (Execution|Attach).
Made more visible when gdb is busy using a gear wheel in the upper right
corner of the window like kfm.
Made the KTreeView widget more flexible by adding a bunch of virtual
keywords. (No, this doesn't have any influence on the look and feel of
KDbg.) While doing that, I fixed a small repainting bug.
ChangeLog starts here.
kdbg-2.5.4/README 0000664 0000000 0000000 00000001014 12237506721 0013312 0 ustar 00root root 0000000 0000000 This is KDbg, a graphical user interface around gdb using
KDE, the K Desktop Environment.
To install:
cmake -DCMAKE_INSTALL_PREFIX=/opt/kde4 .
make
sudo make install
(Make sure your KDE is installed in /opt/kde4; adjust the path
to suit your system. On some systems it is /usr.)
Problem reports as well as success stories are welcome!
The homepage is at
http://www.kdbg.org/
You'll find the most recent version of KDbg there as well as
other useful information concerning debugging.
Johannes Sixt
kdbg-2.5.4/ReleaseNotes-2.2.0 0000664 0000000 0000000 00000002133 12237506721 0015406 0 ustar 00root root 0000000 0000000 KDbg Release Notes for version 2.2.0
====================================
The 2.2.x series is still based on Qt 3 and KDE 3.
Changes since 2.1.1
-------------------
Features:
- Source code windows have now a tab attached, which makes switching
source files much easier.
- Source code windows now show line numbers at the left.
- There are now "Find Next" and "Find Previous" commands with shortcuts
F3 and Shift+F3.
- Improved support of template types in the type tables (which are used
to show structure members next to a structure variable). Notably, the
number of elements in STL and Qt collection classes are shown.
- Arguments for the debugged program can be passed on KDbg's command line.
Bug fixes
- An incorrect terminal command string setting could crash KDbg if it
contained format specifiers other than exactly one '%s'.
- The format specifier in the memory dump window was not correctly
preserved when the expression is changed.
- Setting a conditional breakpoint could crash KDbg.
- Using Attach on systems that use the simplified Attach to Process dialog
could crash KDbg.
kdbg-2.5.4/ReleaseNotes-2.2.1 0000664 0000000 0000000 00000001104 12237506721 0015404 0 ustar 00root root 0000000 0000000 KDbg Release Notes for version 2.2.1
====================================
The 2.2.x series is still based on Qt 3 and KDE 3.
Changes since 2.2.0
-------------------
Bug fixes
- Compilation with newer glibc failed.
- A crash could occur when the variable window was updated.
- A crash when command line switch -a was used together with a non-existing
executable name.
- Syntax highlighting was applied to all files, not just C/C++.
- The display was incorrect when a file was reloaded that had disassembly
lines visible.
There are also some minor documentation fixes.
kdbg-2.5.4/ReleaseNotes-2.2.2 0000664 0000000 0000000 00000000422 12237506721 0015407 0 ustar 00root root 0000000 0000000 KDbg Release Notes for version 2.2.2
====================================
The 2.2.x series is still based on Qt 3 and KDE 3.
Changes since 2.2.1
-------------------
There is only one bug fix:
- An error message was shown instead of assembler code when gdb 7.1 was used.
kdbg-2.5.4/ReleaseNotes-2.5.0 0000664 0000000 0000000 00000001457 12237506721 0015421 0 ustar 00root root 0000000 0000000 KDbg Release Notes for version 2.5.0
====================================
This release is based on KDE4 and Qt4.
Changes since 2.2.2
-------------------
- A number of icons were exchanged with Oxygen icons. These are not part
of KDbg's source code.
- Session state per debugged program is now stored in a section in $KDEHOME
rather than in a .kdbgrc file in the program's directory. This allows to
debug programs that are located in unwritable directories. But this also
means that earlier session information is disregarded.
- More accurate parsing of GDB responses of various commands fixed bugs in
certain areas, in particular, temporary breakpoints, register values,
truncated struct values, disassembly (again).
- "View Code" from the breakpoint list can open the source code in more cases.
kdbg-2.5.4/ReleaseNotes-2.5.1 0000664 0000000 0000000 00000001040 12237506721 0015406 0 ustar 00root root 0000000 0000000 KDbg Release Notes for version 2.5.1
====================================
Changes since 2.5.0
-------------------
Minor feature enhancements
- .hpp files undergo syntax highlighting.
- Keys j and k can be used to move the cursor position in the source code.
Bug fixes
- Cooperation with newer GDB (7.2 and 7.3) is improved:
. wchar_t strings as printed by GDB 7.2 are recognized;
. the thread list was missing with GDB 7.3;
. program exit was not detected (also GDB 7.3).
- Enum values in anonymous namespaces are now recognized.
kdbg-2.5.4/ReleaseNotes-2.5.2 0000664 0000000 0000000 00000000470 12237506721 0015415 0 ustar 00root root 0000000 0000000 KDbg Release Notes for version 2.5.2
====================================
Changes since 2.5.1
-------------------
Bug fixes
- Support for GDB 7.5.
- More of GDB's output is recognized in some corner cases
- The thread list parser introduced in 2.5.1 sometimes stripped two letters
from the function name.
kdbg-2.5.4/ReleaseNotes-2.5.3 0000664 0000000 0000000 00000001067 12237506721 0015421 0 ustar 00root root 0000000 0000000 KDbg Release Notes for version 2.5.3
====================================
Changes since 2.5.2
-------------------
Bug fixes
- Duplicated and breakpoints do not increase the list of
breakpoints each time a session is started
- Communication with a localized GDB works; this is achieved by setting
LC_ALL=C, which is also propagated to the program being debugged. If
The program needs a different locale, set it in Execution->Arguments,
page Environment.
- Fixed a crash in the memory window
- Updates of the Russian and Croatian translations.
kdbg-2.5.4/ReleaseNotes-2.5.4 0000664 0000000 0000000 00000001103 12237506721 0015411 0 ustar 00root root 0000000 0000000 KDbg Release Notes for version 2.5.4
====================================
Changes since 2.5.3
-------------------
Bug fixes
- Source file names with international characters are handled better.
- When an executable is loaded, GDBs of different vintage print different
text, some of which were treated as error text incorrectly, leading to
failed debugging sessions. More of these texts are now ignored.
- Variables pointing to some global variable lacked the ability to be
expanded with recent GDBs.
- Parsing of string values residing in global variables was fixed.
kdbg-2.5.4/TODO 0000664 0000000 0000000 00000002756 12237506721 0013140 0 ustar 00root root 0000000 0000000 7. Show a list of files for the executable.
9. Use gnuclient to show source code in emacs.
10. Provide formatting options in watch window via mouse, not /FMT.
13. Remember which subtrees have been expanded even when function is left and
reentered.
14. Speed up direct members by reusing values that are already available in
members.
19. Improve support of breakpoints in source files having directories in their
names.
22. Allow to change whether signals are handled or ignored.
24. Allow to view fewer or more bytes in the memory dump. Beautify the display.
25. Automatic single-stepping by instruction when a source line is disassembled.
26. Let the user hide some members of certain structures (on a per-type basis).
The parent indicates whether all members are visible. Provide a context menu
to display the hidden entries. Save the settings somewhere.
28. Better support for remote debugging and special versions of gdb, e.g.
"target pilot localhost:2000".
29. "Crash report" function: Stack trace with excerpts of the source code and
variable dumps for every frame.
30. Grey out watch window expressions if there are variables that are not
in scope.
31. Show the memory dump in a combined ASCII and hex view.
32. Allow to manipulate memory in the memory dump window
33. Clear the status bar when the program is (re-)started (because if this takes
some time, you don't know whether KDbg does something.)
35. Allow to copy variable values to the clipboard.
36.
kdbg-2.5.4/kdbg.spec 0000664 0000000 0000000 00000003127 12237506721 0014224 0 ustar 00root root 0000000 0000000 %define name kdbg
%define version 2.0.0
%define release 1.kde2
%define prefix /usr
%define builddir $RPM_BUILD_DIR/%{name}-%{version}
Summary: KDbg - KDE Debugging GUI around gdb
Name: %{name}
Version: %{version}
Release: %{release}
Prefix: %{prefix}
Group: X11/KDE/Development
Copyright: GPL
Distribution: RedHat 7.0
Vendor: Johannes Sixt
Packager: Ullrich von Bassewitz
Source: %{name}-%{version}.tar.gz
URL: http://www.kdbg.org/
Requires: kdelibs >= 2.0
BuildRoot: /tmp/build-%{name}-%{version}
%description
KDbg is a graphical user interface to gdb, the GNU debugger. It provides
an intuitive interface for setting breakpoints, inspecting variables, and
stepping through code.
%prep
rm -rf $RPM_BUILD_ROOT
rm -rf %{builddir}
%setup
touch `find . -type f`
%build
if [ -z "$KDEDIR" ]; then
export KDEDIR=%{prefix}
fi
CXXFLAGS="$RPM_OPT_FLAGS" CFLAGS="$RPM_OPT_FLAGS" ./configure \
--prefix=$KDEDIR
make
%install
if [ -z "$KDEDIR" ]; then
export KDEDIR=%{prefix}
fi
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install-strip
cd $RPM_BUILD_ROOT
find . -type d | sed '1,2d;s,^\.,\%attr(-\,root\,root) \%dir ,' > \
$RPM_BUILD_DIR/file.list.%{name}
find . -type f | sed -e 's,^\.,\%attr(-\,root\,root) ,' \
-e '/\/config\//s|^|%config|' >> \
$RPM_BUILD_DIR/file.list.%{name}
find . -type l | sed 's,^\.,\%attr(-\,root\,root) ,' >> \
$RPM_BUILD_DIR/file.list.%{name}
echo "%docdir $KDEDIR/share/doc/HTML" >> $RPM_BUILD_DIR/file.list.%{name}
%clean
rm -rf $RPM_BUILD_ROOT
rm -rf %{builddir}
rm -f $RPM_BUILD_DIR/file.list.%{name}
%files -f ../file.list.%{name}
kdbg-2.5.4/kdbg/ 0000775 0000000 0000000 00000000000 12237506721 0013345 5 ustar 00root root 0000000 0000000 kdbg-2.5.4/kdbg/CMakeLists.txt 0000664 0000000 0000000 00000005165 12237506721 0016114 0 ustar 00root root 0000000 0000000 add_subdirectory(doc)
add_subdirectory(pics)
add_subdirectory(typetables)
include_directories(${KDE4_INCLUDES} ${KDE4_INCLUDE_DIR} ${QT_INCLUDES} )
include(CheckFunctionExists)
CHECK_INCLUDE_FILES(pty.h HAVE_PTY_H)
CHECK_INCLUDE_FILES(libutil.h HAVE_LIBUTIL_H)
CHECK_INCLUDE_FILES(util.h HAVE_UTIL_H)
CHECK_LIBRARY_EXISTS(util openpty "" HAVE_LIB_UTIL)
if (HAVE_LIB_UTIL)
set(CMAKE_REQUIRED_LIBRARIES util)
endif (HAVE_LIB_UTIL)
CHECK_FUNCTION_EXISTS(openpty HAVE_FUNC_OPENPTY)
message("-- Looking for a suitable 'ps' invocation")
FIND_PROGRAM(PROG_PS ps)
IF (PROG_PS)
set(PS_ARGS -eo pid,ppid,uid,vsz,etime,time,args)
execute_process(COMMAND ${PROG_PS} ${PS_ARGS}
RESULT_VARIABLE PS_FAILED
OUTPUT_QUIET ERROR_QUIET)
IF (NOT PS_FAILED)
execute_process(
COMMAND ${PROG_PS} ${PS_ARGS}
COMMAND sed -e "s/ */ /g" -e 1q
OUTPUT_VARIABLE PS_HEADER)
string(STRIP "${PS_HEADER}" PS_HEADER)
IF (PS_HEADER STREQUAL "PID PPID UID VSZ ELAPSED TIME COMMAND")
# enclose arguments in double-quotes
set(PS_COMMAND \"${PROG_PS}\")
set(PS_MSG ${PROG_PS})
foreach (I ${PS_ARGS})
set(PS_COMMAND ${PS_COMMAND},\"${I}\")
set(PS_MSG "${PS_MSG} ${I}")
endforeach (I)
message("-- Found 'ps' command: ${PS_MSG}")
ENDIF (PS_HEADER STREQUAL "PID PPID UID VSZ ELAPSED TIME COMMAND")
ENDIF (NOT PS_FAILED)
ENDIF (PROG_PS)
IF (NOT PS_COMMAND)
message("-- Looking for a suitable 'ps' invocation - not found")
ENDIF (NOT PS_COMMAND)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
set(kdbg_SRCS
pgmargs.cpp
procattach.cpp
debugger.cpp
dbgdriver.cpp
gdbdriver.cpp
xsldbgdriver.cpp
brkpt.cpp
exprwnd.cpp
regwnd.cpp
memwindow.cpp
threadlist.cpp
sourcewnd.cpp
winstack.cpp
ttywnd.cpp
typetable.cpp
prefdebugger.cpp
prefmisc.cpp
pgmsettings.cpp
watchwindow.cpp
dbgmainwnd.cpp
main.cpp
)
set(kdbg_UI
brkptbase.ui
brkptcondition.ui
pgmargsbase.ui
procattachbase.ui
)
kde4_add_ui_files(kdbg_SRCS ${kdbg_UI})
kde4_add_app_icon(kdbg_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/pics/hi*-app-kdbg.png")
kde4_add_app_icon(kdbg_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/pics/lo*-app-kdbg.png")
kde4_add_executable(kdbg ${kdbg_SRCS})
IF (HAVE_LIB_UTIL)
set(LIB_UTIL util)
ENDIF (HAVE_LIB_UTIL)
target_link_libraries(kdbg ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} ${LIB_UTIL})
install(TARGETS kdbg ${INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES kdbg.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
install(FILES kdbgrc DESTINATION ${CONFIG_INSTALL_DIR})
install(FILES kdbgui.rc DESTINATION ${DATA_INSTALL_DIR}/kdbg)
kdbg-2.5.4/kdbg/brkpt.cpp 0000664 0000000 0000000 00000022413 12237506721 0015175 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#include
#include /* i18n */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "debugger.h"
#include "brkpt.h"
#include "dbgdriver.h"
#include
#include
#include "mydebug.h"
#include "ui_brkptcondition.h"
class BreakpointItem : public QTreeWidgetItem, public Breakpoint
{
public:
BreakpointItem(QTreeWidget* list, const Breakpoint& bp);
void updateFrom(const Breakpoint& bp);
void display(); /* sets icon and visible texts */
bool enabled() const { return Breakpoint::enabled; }
};
BreakpointTable::BreakpointTable(QWidget* parent) :
QWidget(parent),
m_debugger(0)
{
m_ui.setupUi(this);
connect(m_ui.bpEdit, SIGNAL(returnPressed()),
this, SLOT(on_btAddBP_clicked()));
initListAndIcons();
connect(m_ui.bpList, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
this, SLOT(updateUI()));
// double click on item is same as View code
connect(m_ui.bpList,SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
this, SLOT(on_btViewCode_clicked()));
// need mouse button events
m_ui.bpList->viewport()->installEventFilter(this);
}
BreakpointTable::~BreakpointTable()
{
}
void BreakpointTable::updateBreakList()
{
std::list deletedItems;
for (int i = 0 ; i < m_ui.bpList->topLevelItemCount(); i++)
{
deletedItems.push_back(static_cast(m_ui.bpList->topLevelItem(i)));
}
// get the new list
for (KDebugger::BrkptROIterator bp = m_debugger->breakpointsBegin(); bp != m_debugger->breakpointsEnd(); ++bp)
{
// look up this item
for (std::list::iterator o = deletedItems.begin(); o != deletedItems.end(); ++o)
{
if ((*o)->id == bp->id) {
(*o)->updateFrom(*bp);
deletedItems.erase(o); /* don't delete */
goto nextItem;
}
}
// not in the list; add it
new BreakpointItem(m_ui.bpList,*bp);
nextItem:;
}
// delete all untouched breakpoints
while (!deletedItems.empty()) {
delete deletedItems.front();
deletedItems.pop_front();
}
}
BreakpointItem::BreakpointItem(QTreeWidget* list, const Breakpoint& bp) :
QTreeWidgetItem(list),
Breakpoint(bp)
{
display();
}
void BreakpointItem::updateFrom(const Breakpoint& bp)
{
Breakpoint::operator=(bp); /* assign new values */
display();
}
void BreakpointTable::on_btAddBP_clicked()
{
// set a breakpoint at the specified text
QString bpText = m_ui.bpEdit->text();
bpText = bpText.trimmed();
if (m_debugger->isReady())
{
Breakpoint* bp = new Breakpoint;
bp->text = bpText;
m_debugger->setBreakpoint(bp, false);
}
}
void BreakpointTable::on_btAddWP_clicked()
{
// set a watchpoint for the specified expression
QString wpExpr = m_ui.bpEdit->text();
wpExpr = wpExpr.trimmed();
if (m_debugger->isReady()) {
Breakpoint* bp = new Breakpoint;
bp->type = Breakpoint::watchpoint;
bp->text = wpExpr;
m_debugger->setBreakpoint(bp, false);
}
}
void BreakpointTable::on_btRemove_clicked()
{
BreakpointItem* bp = static_cast(m_ui.bpList->currentItem());
if (bp != 0) {
m_debugger->deleteBreakpoint(bp->id);
// note that bp may be deleted by now
// (if bp was an orphaned breakpoint)
}
}
void BreakpointTable::on_btEnaDis_clicked()
{
BreakpointItem* bp = static_cast(m_ui.bpList->currentItem());
if (bp != 0) {
m_debugger->enableDisableBreakpoint(bp->id);
}
}
void BreakpointTable::on_btViewCode_clicked()
{
BreakpointItem* bp = static_cast(m_ui.bpList->currentItem());
if (bp == 0)
return;
if (!m_debugger->infoLine(bp->fileName, bp->lineNo, bp->address))
emit activateFileLine(bp->fileName, bp->lineNo, bp->address);
}
void BreakpointTable::updateUI()
{
bool enableChkpt = m_debugger->canChangeBreakpoints();
m_ui.btAddBP->setEnabled(enableChkpt);
m_ui.btAddWP->setEnabled(enableChkpt);
BreakpointItem* bp = static_cast(m_ui.bpList->currentItem());
m_ui.btViewCode->setEnabled(bp != 0);
if (bp == 0) {
enableChkpt = false;
} else {
if (bp->enabled()) {
m_ui.btEnaDis->setText(i18n("&Disable"));
} else {
m_ui.btEnaDis->setText(i18n("&Enable"));
}
}
m_ui.btRemove->setEnabled(enableChkpt);
m_ui.btEnaDis->setEnabled(enableChkpt);
m_ui.btConditional->setEnabled(enableChkpt);
}
bool BreakpointTable::eventFilter(QObject* ob, QEvent* ev)
{
if (ev->type() == QEvent::MouseButtonPress)
{
QMouseEvent* mev = static_cast(ev);
if (mev->button() == Qt::MidButton) {
// enable or disable the clicked-on item
BreakpointItem* bp =
static_cast(m_ui.bpList->itemAt(mev->pos()));
if (bp != 0)
{
m_debugger->enableDisableBreakpoint(bp->id);
}
return true;
}
}
return QWidget::eventFilter(ob, ev);
}
class ConditionalDlg : public QDialog
{
private:
Ui::BrkPtCondition m_ui;
public:
ConditionalDlg(QWidget* parent);
~ConditionalDlg();
void setCondition(const QString& text) { m_ui.condition->setText(text); }
QString condition() { return m_ui.condition->text(); }
void setIgnoreCount(uint count){ m_ui.ignoreCount->setValue(count); };
uint ignoreCount(){ return m_ui.ignoreCount->value(); };
};
void BreakpointTable::on_btConditional_clicked()
{
BreakpointItem* bp = static_cast(m_ui.bpList->currentItem());
if (bp == 0)
return;
/*
* Important: we must not keep a pointer to the Breakpoint around,
* since it may vanish while the modal dialog is open through other
* user interactions (like clicking at the breakpoint in the source
* window)!
*/
int id = bp->id;
ConditionalDlg dlg(this);
dlg.setCondition(bp->condition);
dlg.setIgnoreCount(bp->ignoreCount);
if (dlg.exec() != QDialog::Accepted)
return;
QString conditionInput = dlg.condition();
int ignoreCount = dlg.ignoreCount();
m_debugger->conditionalBreakpoint(id, conditionInput, ignoreCount);
}
void BreakpointTable::initListAndIcons()
{
m_ui.bpList->setColumnWidth(0, 220);
m_ui.bpList->setColumnWidth(1, 65);
m_ui.bpList->setColumnWidth(2, 30);
m_ui.bpList->setColumnWidth(3, 30);
m_ui.bpList->setColumnWidth(4, 200);
// add pixmaps
QPixmap brkena = UserIcon("brkena");
QPixmap brkdis = UserIcon("brkdis");
QPixmap watchena = UserIcon("watchena");
QPixmap watchdis = UserIcon("watchdis");
QPixmap brktmp = UserIcon("brktmp");
QPixmap brkcond = UserIcon("brkcond");
QPixmap brkorph = UserIcon("brkorph");
/*
* There are 32 different pixmaps: The basic enabled or disabled
* breakpoint, plus an optional overlaid brktmp icon plus an optional
* overlaid brkcond icon, plus an optional overlaid brkorph icon. Then
* the same sequence for watchpoints.
*/
m_icons.resize(32);
QPixmap canvas(16,16);
for (int i = 0; i < 32; i++) {
{
QPainter p(&canvas);
// clear canvas
p.fillRect(0,0, canvas.width(),canvas.height(), Qt::cyan);
// basic icon
if (i & 1) {
p.drawPixmap(1,1, (i & 8) ? watchena : brkena);
} else {
p.drawPixmap(1,1, (i & 8) ? watchdis : brkdis);
}
// temporary overlay
if (i & 2) {
p.drawPixmap(1,1, brktmp);
}
// conditional overlay
if (i & 4) {
p.drawPixmap(1,1, brkcond);
}
// orphan overlay
if (i & 16) {
p.drawPixmap(1,1, brkorph);
}
}
canvas.setMask(canvas.createHeuristicMask());
m_icons[i] = QIcon(canvas);
}
}
void BreakpointItem::display()
{
BreakpointTable* lb = static_cast(treeWidget()->parent());
/* breakpoint icon code; keep order the same as in BreakpointTable::initListAndIcons */
int code = enabled() ? 1 : 0;
if (temporary)
code += 2;
if (!condition.isEmpty() || ignoreCount > 0)
code += 4;
if (Breakpoint::type == watchpoint)
code += 8;
if (isOrphaned())
code += 16;
setIcon(0, lb->m_icons[code]);
// more breakpoint info
if (!location.isEmpty()) {
setText(0, location);
} else if (!Breakpoint::text.isEmpty()) {
setText(0, Breakpoint::text);
} else if (!fileName.isEmpty()) {
// use only the file name portion
QString file = QFileInfo(fileName).fileName();
// correct zero-based line-numbers
setText(0, file + ":" + QString::number(lineNo+1));
} else {
setText(0, "*" + address.asString());
}
int c = 0;
setText(++c, address.asString());
QString tmp;
if (hitCount == 0) {
setText(++c, QString());
} else {
tmp.setNum(hitCount);
setText(++c, tmp);
}
if (ignoreCount == 0) {
setText(++c, QString());
} else {
tmp.setNum(ignoreCount);
setText(++c, tmp);
}
if (condition.isEmpty()) {
setText(++c, QString());
} else {
setText(++c, condition);
}
}
ConditionalDlg::ConditionalDlg(QWidget* parent) :
QDialog(parent)
{
m_ui.setupUi(this);
QString title = KGlobal::caption();
title += i18n(": Conditional breakpoint");
setWindowTitle(title);
}
ConditionalDlg::~ConditionalDlg()
{
}
#include "brkpt.moc"
kdbg-2.5.4/kdbg/brkpt.h 0000664 0000000 0000000 00000003334 12237506721 0014643 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#ifndef BRKPT_H
#define BRKPT_H
#include
#include
#include
#include "ui_brkptbase.h"
class KDebugger;
class BreakpointItem;
class BreakpointTable : public QWidget
{
Q_OBJECT
public:
BreakpointTable(QWidget* parent);
~BreakpointTable();
void setDebugger(KDebugger* deb) { m_debugger = deb; }
protected:
KDebugger* m_debugger;
Ui::BrkPtBase m_ui;
std::vector m_icons;
void insertBreakpoint(int num, bool temp, bool enabled, QString location,
QString fileName = 0, int lineNo = -1,
int hits = 0, uint ignoreCount = 0,
QString condition = QString());
void initListAndIcons();
virtual bool eventFilter(QObject* ob, QEvent* ev);
friend class BreakpointItem;
signals:
/**
* This signal is emitted when the user wants to go to the source code
* where the current breakpoint is in.
*
* @param file specifies the file; this is not necessarily a full path
* name, and if it is relative, you won't know relative to what, you
* can only guess.
* @param lineNo specifies the line number (0-based!).
* @param address specifies the exact address of the breakpoint.
*/
void activateFileLine(const QString& file, int lineNo, const DbgAddr& address);
public slots:
void on_btAddBP_clicked();
void on_btAddWP_clicked();
void on_btRemove_clicked();
void on_btEnaDis_clicked();
void on_btViewCode_clicked();
void on_btConditional_clicked();
void updateUI();
void updateBreakList();
};
#endif // BRKPT_H
kdbg-2.5.4/kdbg/brkptbase.ui 0000664 0000000 0000000 00000006537 12237506721 0015674 0 ustar 00root root 0000000 0000000 BrkPtBase00591300FormtruefalseLocationAddressHitsIgnoreConditionAdd &BreakpointAdd &Watchpoint&Remove&Disable&View Code&Conditional...Qt::Vertical2040KLineEditQLineEditklineedit.hbpEditbpListbtAddBPbtAddWPbtRemovebtEnaDisbtViewCodebtConditional
kdbg-2.5.4/kdbg/brkptcondition.ui 0000664 0000000 0000000 00000005716 12237506721 0016746 0 ustar 00root root 0000000 0000000 BrkPtCondition0040098Dialog50false&Condition:conditionIgnore &next hits:ignoreCountdo not ignoreQAbstractSpinBox::CorrectToNearestValue999Qt::HorizontalQDialogButtonBox::Cancel|QDialogButtonBox::OkKLineEditQLineEditklineedit.hbuttonBoxaccepted()BrkPtConditionaccept()2227215786buttonBoxrejected()BrkPtConditionreject()2907828686
kdbg-2.5.4/kdbg/commandids.h 0000664 0000000 0000000 00000000501 12237506721 0015630 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#ifndef COMMANDIDS_H
#define COMMANDIDS_H
// statusbar ids
#define ID_STATUS_MSG 191
#define ID_STATUS_ACTIVE 193
#endif // COMMANDIDS_H
kdbg-2.5.4/kdbg/config.h.cmake 0000664 0000000 0000000 00000000343 12237506721 0016042 0 ustar 00root root 0000000 0000000 #ifndef CONFIG_H_Included
#define CONFIG_H_Included
#cmakedefine HAVE_FUNC_OPENPTY
#cmakedefine HAVE_LIBUTIL_H
#cmakedefine HAVE_PTY_H
#cmakedefine HAVE_UTIL_H
#cmakedefine PS_COMMAND @PS_COMMAND@
#endif // CONFIG_H_Included
kdbg-2.5.4/kdbg/dbgdriver.cpp 0000664 0000000 0000000 00000025702 12237506721 0016027 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#include "dbgdriver.h"
#include "exprwnd.h"
#include
#include
#include
#include /* strtol, atoi */
#include
#include "mydebug.h"
#include
DebuggerDriver::DebuggerDriver() :
m_state(DSidle),
m_activeCmd(0)
{
// debugger process
connect(this, SIGNAL(readyReadStandardOutput()), SLOT(slotReceiveOutput()));
connect(this, SIGNAL(bytesWritten(qint64)), SLOT(slotCommandRead()));
connect(this, SIGNAL(finished(int, QProcess::ExitStatus)),
SLOT(slotExited()));
}
DebuggerDriver::~DebuggerDriver()
{
flushHiPriQueue();
flushLoPriQueue();
}
bool DebuggerDriver::startup(QString cmdStr)
{
// clear command queues
delete m_activeCmd;
m_activeCmd = 0;
flushHiPriQueue();
flushLoPriQueue();
m_state = DSidle;
// debugger executable
if (cmdStr.isEmpty())
cmdStr = defaultInvocation();
QStringList cmd = cmdStr.split(' ', QString::SkipEmptyParts);
if (cmd.isEmpty())
return false;
QString pgm = cmd.takeFirst();
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert(QLatin1String("LC_ALL"), QLatin1String("C"));
env.remove(QLatin1String("LANG"));
setProcessEnvironment(env);
#endif
setProcessChannelMode(MergedChannels);
start(pgm, cmd);
if (!waitForStarted(-1))
return false;
// open log file
if (!m_logFile.isOpen() && !m_logFileName.isEmpty()) {
m_logFile.setFileName(m_logFileName);
m_logFile.open(QIODevice::WriteOnly);
}
return true;
}
void DebuggerDriver::slotExited()
{
static const char txt[] = "\n====== debugger exited ======\n";
if (m_logFile.isOpen()) {
m_logFile.write(txt,sizeof(txt)-1);
}
// reset state
m_state = DSidle;
// empty buffer
m_output.clear();
}
CmdQueueItem* DebuggerDriver::executeCmdString(DbgCommand cmd,
QString cmdString, bool clearLow)
{
// place a new command into the high-priority queue
CmdQueueItem* cmdItem = new CmdQueueItem(cmd, cmdString);
m_hipriCmdQueue.push(cmdItem);
if (clearLow) {
if (m_state == DSrunningLow) {
// take the liberty to interrupt the running command
m_state = DSinterrupted;
::kill(pid(), SIGINT);
ASSERT(m_activeCmd != 0);
TRACE(QString().sprintf("interrupted the command %d",
(m_activeCmd ? m_activeCmd->m_cmd : -1)));
delete m_activeCmd;
m_activeCmd = 0;
}
flushLoPriQueue();
}
// if gdb is idle, send it the command
if (m_state == DSidle) {
ASSERT(m_activeCmd == 0);
writeCommand();
}
return cmdItem;
}
bool CmdQueueItem::IsEqualCmd::operator()(CmdQueueItem* cmd) const
{
return cmd->m_cmd == m_cmd && cmd->m_cmdString == m_str;
}
CmdQueueItem* DebuggerDriver::queueCmdString(DbgCommand cmd,
QString cmdString, QueueMode mode)
{
// place a new command into the low-priority queue
std::list::iterator i;
CmdQueueItem* cmdItem = 0;
switch (mode) {
case QMoverrideMoreEqual:
case QMoverride:
// check whether gdb is currently processing this command
if (m_activeCmd != 0 &&
m_activeCmd->m_cmd == cmd && m_activeCmd->m_cmdString == cmdString)
{
return m_activeCmd;
}
// check whether there is already the same command in the queue
i = find_if(m_lopriCmdQueue.begin(), m_lopriCmdQueue.end(), CmdQueueItem::IsEqualCmd(cmd, cmdString));
if (i != m_lopriCmdQueue.end()) {
// found one
cmdItem = *i;
if (mode == QMoverrideMoreEqual) {
// All commands are equal, but some are more equal than others...
// put this command in front of all others
m_lopriCmdQueue.erase(i);
m_lopriCmdQueue.push_front(cmdItem);
}
break;
} // else none found, so add it
// drop through
case QMnormal:
cmdItem = new CmdQueueItem(cmd, cmdString);
m_lopriCmdQueue.push_back(cmdItem);
}
// if gdb is idle, send it the command
if (m_state == DSidle) {
ASSERT(m_activeCmd == 0);
writeCommand();
}
return cmdItem;
}
// dequeue a pending command, make it the active one and send it to gdb
void DebuggerDriver::writeCommand()
{
// ASSERT(m_activeCmd == 0);
assert(m_activeCmd == 0);
// first check the high-priority queue - only if it is empty
// use a low-priority command.
CmdQueueItem* cmd;
DebuggerState newState = DScommandSent;
if (!m_hipriCmdQueue.empty()) {
cmd = m_hipriCmdQueue.front();
m_hipriCmdQueue.pop();
} else if (!m_lopriCmdQueue.empty()) {
cmd = m_lopriCmdQueue.front();
m_lopriCmdQueue.pop_front();
newState = DScommandSentLow;
} else {
// nothing to do
m_state = DSidle; /* is necessary if command was interrupted earlier */
return;
}
m_activeCmd = cmd;
TRACE("in writeCommand: " + cmd->m_cmdString);
QByteArray str = cmd->m_cmdString.toLocal8Bit();
const char* data = str.data();
qint64 len = str.length();
while (len > 0) {
qint64 n = write(data, len);
if (n <= 0)
break; // ignore error
len -= n;
data += n;
}
// write also to log file
if (m_logFile.isOpen()) {
m_logFile.write(str);
m_logFile.flush();
}
m_state = newState;
}
void DebuggerDriver::flushLoPriQueue()
{
while (!m_lopriCmdQueue.empty()) {
delete m_lopriCmdQueue.back();
m_lopriCmdQueue.pop_back();
}
}
void DebuggerDriver::flushHiPriQueue()
{
while (!m_hipriCmdQueue.empty()) {
delete m_hipriCmdQueue.front();
m_hipriCmdQueue.pop();
}
}
void DebuggerDriver::flushCommands(bool hipriOnly)
{
flushHiPriQueue();
if (!hipriOnly) {
flushLoPriQueue();
}
}
void DebuggerDriver::slotCommandRead()
{
TRACE(__PRETTY_FUNCTION__);
// there must be an active command which is not yet commited
ASSERT(m_state == DScommandSent || m_state == DScommandSentLow);
ASSERT(m_activeCmd != 0);
ASSERT(!m_activeCmd->m_committed);
// commit the command
m_activeCmd->m_committed = true;
// now the debugger is officially working on the command
m_state = m_state == DScommandSent ? DSrunning : DSrunningLow;
// set the flag that reflects whether the program is really running
switch (m_activeCmd->m_cmd) {
case DCrun: case DCcont: case DCnext: case DCstep: case DCfinish: case DCuntil:
emit inferiorRunning();
break;
default:
break;
}
// process delayed output
while (!m_delayedOutput.empty()) {
QByteArray delayed = m_delayedOutput.front();
m_delayedOutput.pop();
processOutput(delayed);
}
}
void DebuggerDriver::slotReceiveOutput()
{
QByteArray data = readAllStandardOutput();
/*
* The debugger should be running (processing a command) at this point.
* If it is not, it is still idle because we haven't received the
* bytesWritten signal yet, in which case there must be an active command
* which is not commited.
*/
if (m_state == DScommandSent || m_state == DScommandSentLow) {
ASSERT(m_activeCmd != 0);
ASSERT(!m_activeCmd->m_committed);
/*
* We received output before we got signal bytesWritten. Collect this
* output, it will be processed by commandRead when it gets the
* acknowledgment for the uncommitted command.
*/
m_delayedOutput.push(data);
return;
}
processOutput(data);
}
void DebuggerDriver::processOutput(const QByteArray& data)
{
// write to log file (do not log delayed output - it would appear twice)
if (m_logFile.isOpen()) {
m_logFile.write(data);
m_logFile.flush();
}
/*
* gdb sometimes produces stray output while it's idle. This happens if
* it receives a signal, most prominently a SIGCONT after a SIGTSTP:
* The user haltet kdbg with Ctrl-Z, then continues it with "fg", which
* also continues gdb, which repeats the prompt!
*/
if (m_activeCmd == 0 && m_state != DSinterrupted) {
// ignore the output
TRACE("ignoring stray output: " + QString(data));
return;
}
ASSERT(m_state == DSrunning || m_state == DSrunningLow || m_state == DSinterrupted);
ASSERT(m_activeCmd != 0 || m_state == DSinterrupted);
// collect output until next prompt string is found
// accumulate it
m_output += data;
// check for a prompt
int promptStart = findPrompt(m_output);
if (promptStart >= 0)
{
// found prompt!
// terminate output before the prompt
m_output.resize(promptStart);
/*
* We've got output for the active command. But if it was
* interrupted, ignore it.
*/
if (m_state != DSinterrupted) {
/*
* m_state shouldn't be DSidle while we are parsing the output
* so that all commands produced by parse() go into the queue
* instead of being written to gdb immediately.
*/
ASSERT(m_state != DSidle);
CmdQueueItem* cmd = m_activeCmd;
m_activeCmd = 0;
commandFinished(cmd);
delete cmd;
}
// empty buffer
m_output.clear();
// also clear delayed output if interrupted
if (m_state == DSinterrupted) {
m_delayedOutput = std::queue();
}
/*
* We parsed some output successfully. Unless there's more delayed
* output, the debugger must be idle now, so send down the next
* command.
*/
if (m_delayedOutput.empty()) {
if (m_hipriCmdQueue.empty() && m_lopriCmdQueue.empty()) {
// no pending commands
m_state = DSidle;
emit enterIdleState();
} else {
writeCommand();
}
}
}
}
void DebuggerDriver::dequeueCmdByVar(VarTree* var)
{
if (var == 0)
return;
std::list::iterator i = m_lopriCmdQueue.begin();
while (i != m_lopriCmdQueue.end()) {
if ((*i)->m_expr != 0 && var->isAncestorEq((*i)->m_expr)) {
// this is indeed a critical command; delete it
TRACE("removing critical lopri-cmd: " + (*i)->m_cmdString);
delete *i;
m_lopriCmdQueue.erase(i++);
} else
++i;
}
}
QString DebuggerDriver::editableValue(VarTree* value)
{
// by default, let the user edit what is visible
return value->value();
}
StackFrame::~StackFrame()
{
delete var;
}
DbgAddr::DbgAddr(const QString& aa) :
a(aa)
{
cleanAddr();
}
/*
* We strip off the leading 0x and any leading zeros.
*/
void DbgAddr::cleanAddr()
{
if (a.isEmpty())
return;
while (a[0] == '0' || a[0] == 'x') {
a.remove(0, 1);
}
}
void DbgAddr::operator=(const QString& aa)
{
a = aa;
fnoffs = QString();
cleanAddr();
}
/* Re-attach 0x in front of the address */
QString DbgAddr::asString() const
{
if (a.isEmpty())
return QString();
else
return "0x" + a;
}
bool operator==(const DbgAddr& a1, const DbgAddr& a2)
{
return QString::compare(a1.a, a2.a) == 0;
}
bool operator>(const DbgAddr& a1, const DbgAddr& a2)
{
if (a1.a.length() > a2.a.length())
return true;
if (a1.a.length() < a2.a.length())
return false;
return QString::compare(a1.a, a2.a) > 0;
}
Breakpoint::Breakpoint() :
id(0),
type(breakpoint),
temporary(false),
enabled(true),
ignoreCount(0),
hitCount(0),
lineNo(0)
{ }
#include "dbgdriver.moc"
kdbg-2.5.4/kdbg/dbgdriver.h 0000664 0000000 0000000 00000043413 12237506721 0015473 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#ifndef DBGDRIVER_H
#define DBGDRIVER_H
#include
#include
#include
#include
#include
class VarTree;
class ExprValue;
class ExprWnd;
class KDebugger;
class QStringList;
/**
* A type representing an address.
*/
struct DbgAddr
{
QString a;
QString fnoffs;
DbgAddr() { }
DbgAddr(const QString& aa);
DbgAddr(const DbgAddr& src) : a(src.a), fnoffs(src.fnoffs) { }
void operator=(const QString& aa);
void operator=(const DbgAddr& src) { a = src.a; fnoffs = src.fnoffs; }
QString asString() const;
bool isEmpty() const { return a.isEmpty(); }
protected:
void cleanAddr();
};
bool operator==(const DbgAddr& a1, const DbgAddr& a2);
bool operator>(const DbgAddr& a1, const DbgAddr& a2);
enum DbgCommand {
DCinitialize,
DCtty,
DCexecutable,
DCtargetremote,
DCcorefile,
DCattach,
DCinfolinemain,
DCinfolocals,
DCinforegisters,
DCexamine,
DCinfoline,
DCdisassemble,
DCsetargs,
DCsetenv,
DCunsetenv,
DCsetoption, /* debugger options */
DCcd,
DCbt,
DCrun,
DCcont,
DCstep,
DCstepi,
DCnext,
DCnexti,
DCfinish,
DCuntil, /* line number is zero-based! */
DCkill,
DCbreaktext,
DCbreakline, /* line number is zero-based! */
DCtbreakline, /* line number is zero-based! */
DCbreakaddr,
DCtbreakaddr,
DCwatchpoint,
DCdelete,
DCenable,
DCdisable,
DCprint,
DCprintDeref,
DCprintStruct,
DCprintQStringStruct,
DCframe,
DCfindType,
DCinfosharedlib,
DCthread,
DCinfothreads,
DCinfobreak,
DCcondition,
DCsetpc,
DCignore,
DCprintWChar,
DCsetvariable
};
enum RunDevNull {
RDNstdin = 0x1, /* redirect stdin to /dev/null */
RDNstdout = 0x2, /* redirect stdout to /dev/null */
RDNstderr = 0x4 /* redirect stderr to /dev/null */
};
/**
* How the memory dump is formated. The lowest 4 bits define the size of
* the entities. The higher bits define how these are formatted. Note that
* not all combinations make sense.
*/
enum MemoryDumpType {
// sizes
MDTbyte = 0x1,
MDThalfword = 0x2,
MDTword = 0x3,
MDTgiantword = 0x4,
MDTsizemask = 0xf,
// formats
MDThex = 0x10,
MDTsigned = 0x20,
MDTunsigned = 0x30,
MDToctal = 0x40,
MDTbinary = 0x50,
MDTaddress = 0x60,
MDTchar = 0x70,
MDTfloat = 0x80,
MDTstring = 0x90,
MDTinsn = 0xa0,
MDTformatmask = 0xf0
};
struct Breakpoint;
/**
* Debugger commands are placed in a queue. Only one command at a time is
* sent down to the debugger. All other commands in the queue are retained
* until the sent command has been processed by gdb. The debugger tells us
* that it's done with the command by sending the prompt. The output of the
* debugger is parsed at that time. Then, if more commands are in the
* queue, the next one is sent to the debugger.
*/
struct CmdQueueItem
{
DbgCommand m_cmd;
QString m_cmdString;
bool m_committed; /* just a debugging aid */
// remember which expression when printing an expression
VarTree* m_expr;
ExprWnd* m_exprWnd;
// remember file position
QString m_fileName;
int m_lineNo;
DbgAddr m_addr;
// the breakpoint info
Breakpoint* m_brkpt;
int m_existingBrkpt;
// whether command was emitted due to direct user request (only set when relevant)
bool m_byUser;
CmdQueueItem(DbgCommand cmd, const QString& str) :
m_cmd(cmd),
m_cmdString(str),
m_committed(false),
m_expr(0),
m_exprWnd(0),
m_lineNo(0),
m_brkpt(0),
m_existingBrkpt(0),
m_byUser(false)
{ }
struct IsEqualCmd
{
IsEqualCmd(DbgCommand cmd, const QString& str) : m_cmd(cmd), m_str(str) { }
bool operator()(CmdQueueItem*) const;
DbgCommand m_cmd;
const QString& m_str;
};
};
/**
* The information about a breakpoint that is parsed from the list of
* breakpoints.
*/
struct Breakpoint
{
int id; /* gdb's number */
enum Type {
breakpoint, watchpoint
} type;
bool temporary;
bool enabled;
QString location;
QString text; /* text if set using DCbreaktext */
DbgAddr address; /* exact address of breakpoint */
QString condition; /* condition as printed by gdb */
int ignoreCount; /* ignore next that may hits */
int hitCount; /* as reported by gdb */
// the following items repeat the location, but in a better usable way
QString fileName;
int lineNo; /* zero-based line number */
Breakpoint();
bool isOrphaned() const { return id < 0; }
};
/**
* Information about a stack frame.
*/
struct FrameInfo
{
QString fileName;
int lineNo; /* zero-based line number */
DbgAddr address; /* exact address of PC */
};
/**
* The information about a stack frame as parsed from the backtrace.
*/
struct StackFrame : FrameInfo
{
int frameNo;
ExprValue* var; /* more information if non-zero */
StackFrame() : var(0) { }
~StackFrame();
};
/**
* The information about a thread as parsed from the threads list.
*/
struct ThreadInfo : FrameInfo
{
int id; /* gdb's number */
QString threadName; /* the SYSTAG */
QString function; /* where thread is halted */
bool hasFocus; /* the thread whose stack we are watching */
};
/**
* Register information
*/
struct RegisterInfo
{
QString regName;
QString rawValue;
QString cookedValue; /* may be empty */
QString type; /* of vector register if not empty */
};
/**
* Disassembled code
*/
struct DisassembledCode
{
DbgAddr address;
QString code;
};
/**
* Memory contents
*/
struct MemoryDump
{
DbgAddr address;
QString dump;
};
/**
* This is an abstract base class for debugger process.
*
* This class represents the debugger program. It provides the low-level
* interface to the commandline debugger. As such it implements the
* commands and parses the output.
*/
class DebuggerDriver : public QProcess
{
Q_OBJECT
public:
DebuggerDriver();
virtual ~DebuggerDriver() = 0;
virtual QString driverName() const = 0;
/**
* Returns the default command string to invoke the debugger driver.
*/
virtual QString defaultInvocation() const = 0;
/**
* Returns a list of options that can be turned on and off.
*/
virtual QStringList boolOptionList() const = 0;
virtual bool startup(QString cmdStr);
void setLogFileName(const QString& fname) { m_logFileName = fname; }
bool isRunning() { return state() != NotRunning; }
protected:
QString m_runCmd;
enum DebuggerState {
DSidle, /* gdb waits for input */
DSinterrupted, /* a command was interrupted */
DSrunningLow, /* gdb is running a low-priority command */
DSrunning, /* gdb waits for program */
DScommandSent, /* command has been sent, we wait for wroteStdin signal */
DScommandSentLow /* low-prioritycommand has been sent */
};
DebuggerState m_state;
public:
bool isIdle() const { return m_state == DSidle; }
/**
* Tells whether a high prority command would be executed immediately.
*/
bool canExecuteImmediately() const { return m_hipriCmdQueue.empty(); }
protected:
QByteArray m_output; // normal gdb output
std::queue m_delayedOutput; // output colleced before signal bytesWritten() arrived
public:
/**
* Enqueues a high-priority command. High-priority commands are
* executed before any low-priority commands. No user interaction is
* possible as long as there is a high-priority command in the queue.
*/
virtual CmdQueueItem* executeCmd(DbgCommand,
bool clearLow = false) = 0;
virtual CmdQueueItem* executeCmd(DbgCommand, QString strArg,
bool clearLow = false) = 0;
virtual CmdQueueItem* executeCmd(DbgCommand, int intArg,
bool clearLow = false) = 0;
virtual CmdQueueItem* executeCmd(DbgCommand, QString strArg, int intArg,
bool clearLow = false) = 0;
virtual CmdQueueItem* executeCmd(DbgCommand, QString strArg1, QString strArg2,
bool clearLow = false) = 0;
virtual CmdQueueItem* executeCmd(DbgCommand, int intArg1, int intArg2,
bool clearLow = false) = 0;
enum QueueMode {
QMnormal, /* queues the command last */
QMoverride, /* removes an already queued command */
QMoverrideMoreEqual /* ditto, also puts the command first in the queue */
};
/**
* Enqueues a low-priority command. Low-priority commands are executed
* after any high-priority commands.
*/
virtual CmdQueueItem* queueCmd(DbgCommand,
QueueMode mode) = 0;
virtual CmdQueueItem* queueCmd(DbgCommand, QString strArg,
QueueMode mode) = 0;
virtual CmdQueueItem* queueCmd(DbgCommand, int intArg,
QueueMode mode) = 0;
virtual CmdQueueItem* queueCmd(DbgCommand, QString strArg, int intArg,
QueueMode mode) = 0;
virtual CmdQueueItem* queueCmd(DbgCommand, QString strArg1, QString strArg2,
QueueMode mode) = 0;
/**
* Flushes the command queues.
* @param hipriOnly if true, only the high priority queue is flushed.
*/
virtual void flushCommands(bool hipriOnly = false);
/**
* Terminates the debugger process.
*/
virtual void terminate() = 0;
/**
* Terminates the debugger process, but also detaches any program that
* it has been attached to.
*/
virtual void detachAndTerminate() = 0;
/**
* Interrupts the debuggee.
*/
virtual void interruptInferior() = 0;
/**
* Specifies the command that prints the QString data.
*/
virtual void setPrintQStringDataCmd(const char* cmd) = 0;
/**
* Parses the output as an array of QChars.
*/
virtual ExprValue* parseQCharArray(const char* output, bool wantErrorValue, bool qt3like) = 0;
/**
* Parses a back-trace (the output of the DCbt command).
*/
virtual void parseBackTrace(const char* output, std::list& stack) = 0;
/**
* Parses the output of the DCframe command;
* @param frameNo Returns the frame number.
* @param file Returns the source file name.
* @param lineNo The zero-based line number.
* @param address Returns the exact address.
* @return false if the frame could not be parsed successfully. The
* output values are undefined in this case.
*/
virtual bool parseFrameChange(const char* output, int& frameNo,
QString& file, int& lineNo, DbgAddr& address) = 0;
/**
* Parses a list of breakpoints.
* @param output The output of the debugger.
* @param brks The list of new #Breakpoint objects. The list
* must initially be empty.
* @return False if there was an error before the first breakpoint
* was found. Even if true is returned, #brks may be empty.
*/
virtual bool parseBreakList(const char* output, std::list& brks) = 0;
/**
* Parses a list of threads.
* @param output The output of the debugger.
* @return The new thread list. There is no indication if there was
* a parse error.
*/
virtual std::list parseThreadList(const char* output) = 0;
/**
* Parses the output when the program stops to see whether this it
* stopped due to a breakpoint.
* @param output The output of the debugger.
* @param id Returns the breakpoint id.
* @param file Returns the file name in which the breakpoint is.
* @param lineNo Returns the zero-based line number of the breakpoint.
* @param address Returns the address of the breakpoint.
* @return False if there was no breakpoint.
*/
virtual bool parseBreakpoint(const char* output, int& id,
QString& file, int& lineNo, QString& address) = 0;
/**
* Parses the output of the DCinfolocals command.
* @param output The output of the debugger.
* @param newVars Receives the parsed variable values. The values are
* simply append()ed to the supplied list.
*/
virtual void parseLocals(const char* output, std::list& newVars) = 0;
/**
* Parses the output of a DCprint or DCprintStruct command.
* @param output The output of the debugger.
* @param wantErrorValue Specifies whether the error message should be
* provided as the value of a NKplain variable. If this is false,
* 0 is returned if the printed value is an error message.
* @return the parsed value. It is 0 if there was a parse error
* or if the output is an error message and #wantErrorValue
* is \c false. The returned object's text() is undefined.
*/
virtual ExprValue* parsePrintExpr(const char* output, bool wantErrorValue) = 0;
/**
* Parses the output of the DCcd command.
* @return false if the message is an error message.
*/
virtual bool parseChangeWD(const char* output, QString& message) = 0;
/**
* Parses the output of the DCexecutable command.
* @return false if an error occured.
*/
virtual bool parseChangeExecutable(const char* output, QString& message) = 0;
/**
* Parses the output of the DCcorefile command.
* @return false if the core file was not loaded successfully.
*/
virtual bool parseCoreFile(const char* output) = 0;
enum StopFlags {
SFrefreshSource = 1, /* refresh of source code is needed */
SFrefreshBreak = 2, /* refresh breakpoints */
SFrefreshThreads = 4, /* refresh thread list */
SFprogramActive = 128 /* program remains active */
};
/**
* Parses the output of commands that execute (a piece of) the program.
* @return The inclusive OR of zero or more of the StopFlags.
*/
virtual uint parseProgramStopped(const char* output, QString& message) = 0;
/**
* Parses the output of the DCsharedlibs command.
*/
virtual QStringList parseSharedLibs(const char* output) = 0;
/**
* Parses the output of the DCfindType command.
* @return true if a type was found.
*/
virtual bool parseFindType(const char* output, QString& type) = 0;
/**
* Parses the output of the DCinforegisters command.
*/
virtual std::list parseRegisters(const char* output) = 0;
/**
* Parses the output of the DCinfoline command. Returns false if the
* two addresses could not be found.
*/
virtual bool parseInfoLine(const char* output,
QString& addrFrom, QString& addrTo) = 0;
/**
* Parses the ouput of the DCdisassemble command.
*/
virtual std::list parseDisassemble(const char* output) = 0;
/**
* Parses a memory dump. Returns an empty string if no error was found;
* otherwise it contains an error message.
*/
virtual QString parseMemoryDump(const char* output, std::list& memdump) = 0;
/**
* Parses the output of the DCsetvariable command. Returns an empty
* string if no error was found; otherwise it contains an error
* message.
*/
virtual QString parseSetVariable(const char* output) = 0;
/**
* Returns a value that the user can edit.
*/
virtual QString editableValue(VarTree* value);
protected:
/** Removes all commands from the low-priority queue. */
void flushLoPriQueue();
/** Removes all commands from the high-priority queue. */
void flushHiPriQueue();
std::queue m_hipriCmdQueue;
std::list m_lopriCmdQueue;
/**
* The active command is kept separately from other pending commands.
*/
CmdQueueItem* m_activeCmd;
/**
* Helper function that queues the given command string in the
* low-priority queue.
*/
CmdQueueItem* queueCmdString(DbgCommand cmd, QString cmdString,
QueueMode mode);
/**
* Helper function that queues the given command string in the
* high-priority queue.
*/
CmdQueueItem* executeCmdString(DbgCommand cmd, QString cmdString,
bool clearLow);
void writeCommand();
virtual void commandFinished(CmdQueueItem* cmd) = 0;
protected:
void processOutput(const QByteArray& data);
/**
* Returns the start of the prompt in \a output or -1.
* \a len specifies the size of \a output, but in addition, the contents
* of \a output are NUL-terminated, i.e., \c output[len] is zero.
*/
virtual int findPrompt(const QByteArray& output) const = 0;
// log file
QString m_logFileName;
QFile m_logFile;
public slots:
void dequeueCmdByVar(VarTree* var);
protected slots:
virtual void slotReceiveOutput();
virtual void slotCommandRead();
virtual void slotExited();
signals:
/**
* This signal is emitted when the output of a command has been fully
* collected and is ready to be interpreted.
*/
void commandReceived(CmdQueueItem* cmd, const char* output);
/**
* This signal is emitted when the debugger recognizes that a specific
* location in a file ought to be displayed.
*
* Gdb's --fullname option supports this for the step, next, frame, and
* run commands (and possibly others).
*
* @param file specifies the file; this is not necessarily a full path
* name, and if it is relative, you won't know relative to what, you
* can only guess.
* @param lineNo specifies the line number (0-based!) (this may be
* negative, in which case the file should be activated, but the line
* should NOT be changed).
* @param address specifies the exact address of the PC or is empty.
*/
void activateFileLine(const QString& file, int lineNo, const DbgAddr& address);
/**
* This signal is emitted when a command that starts the inferior has
* been submitted to the debugger.
*/
void inferiorRunning();
/**
* This signal is emitted when all output from the debugger has been
* consumed and no more commands are in the queues.
*/
void enterIdleState();
};
#endif // DBGDRIVER_H
kdbg-2.5.4/kdbg/dbgmainwnd.cpp 0000664 0000000 0000000 00000115452 12237506721 0016173 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#include
#include /* i18n */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "dbgmainwnd.h"
#include "debugger.h"
#include "commandids.h"
#include "winstack.h"
#include "brkpt.h"
#include "threadlist.h"
#include "memwindow.h"
#include "ttywnd.h"
#include "watchwindow.h"
#include "procattach.h"
#include "prefdebugger.h"
#include "prefmisc.h"
#include "gdbdriver.h"
#include "xsldbgdriver.h"
#include "mydebug.h"
#include /* mknod(2) */
#include /* getpid */
static const char defaultTermCmdStr[] = "xterm -name kdbgio -title %T -e sh -c %C";
static const char defaultSourceFilter[] = "*.c *.cc *.cpp *.c++ *.C *.CC";
static const char defaultHeaderFilter[] = "*.h *.hh *.hpp *.h++";
DebuggerMainWnd::DebuggerMainWnd() :
KXmlGuiWindow(),
m_debugger(0),
#ifdef GDB_TRANSCRIPT
m_transcriptFile(GDB_TRANSCRIPT),
#endif
m_outputTermCmdStr(defaultTermCmdStr),
m_outputTermProc(new QProcess),
m_ttyLevel(-1), /* no tty yet */
m_popForeground(false),
m_backTimeout(1000),
m_tabWidth(0),
m_sourceFilter(defaultSourceFilter),
m_headerFilter(defaultHeaderFilter),
m_animation(0),
m_statusActive(i18n("active"))
{
setDockNestingEnabled(true);
m_filesWindow = new WinStack(this);
setCentralWidget(m_filesWindow);
QDockWidget* dw1 = createDockWidget("Stack", i18n("Stack"));
m_btWindow = new QListWidget(dw1);
dw1->setWidget(m_btWindow);
QDockWidget* dw2 = createDockWidget("Locals", i18n("Locals"));
m_localVariables = new ExprWnd(dw2, i18n("Variable"));
dw2->setWidget(m_localVariables);
QDockWidget* dw3 = createDockWidget("Watches", i18n("Watches"));
m_watches = new WatchWindow(dw3);
dw3->setWidget(m_watches);
QDockWidget* dw4 = createDockWidget("Registers", i18n("Registers"));
m_registers = new RegisterView(dw4);
dw4->setWidget(m_registers);
QDockWidget* dw5 = createDockWidget("Breakpoints", i18n("Breakpoints"));
m_bpTable = new BreakpointTable(dw5);
dw5->setWidget(m_bpTable);
QDockWidget* dw6 = createDockWidget("Output", i18n("Output"));
m_ttyWindow = new TTYWindow(dw6);
dw6->setWidget(m_ttyWindow);
QDockWidget* dw7 = createDockWidget("Threads", i18n("Threads"));
m_threads = new ThreadList(dw7);
dw7->setWidget(m_threads);
QDockWidget* dw8 = createDockWidget("Memory", i18n("Memory"));
m_memoryWindow = new MemoryWindow(dw8);
dw8->setWidget(m_memoryWindow);
m_debugger = new KDebugger(this, m_localVariables, m_watches->watchVariables(), m_btWindow);
connect(m_debugger, SIGNAL(updateStatusMessage()), SLOT(slotNewStatusMsg()));
connect(m_debugger, SIGNAL(updateUI()), SLOT(updateUI()));
connect(m_debugger, SIGNAL(breakpointsChanged()), SLOT(updateLineItems()));
connect(m_debugger, SIGNAL(debuggerStarting()), SLOT(slotDebuggerStarting()));
m_bpTable->setDebugger(m_debugger);
m_memoryWindow->setDebugger(m_debugger);
setStandardToolBarMenuEnabled(true);
initKAction();
initStatusBar();
connect(m_watches, SIGNAL(addWatch()), SLOT(slotAddWatch()));
connect(m_watches, SIGNAL(deleteWatch()), m_debugger, SLOT(slotDeleteWatch()));
connect(m_watches, SIGNAL(textDropped(const QString&)), SLOT(slotAddWatch(const QString&)));
connect(&m_filesWindow->m_findDlg, SIGNAL(closed()), SLOT(updateUI()));
connect(m_filesWindow, SIGNAL(newFileLoaded()),
SLOT(slotNewFileLoaded()));
connect(m_filesWindow, SIGNAL(toggleBreak(const QString&,int,const DbgAddr&,bool)),
this, SLOT(slotToggleBreak(const QString&,int,const DbgAddr&,bool)));
connect(m_filesWindow, SIGNAL(enadisBreak(const QString&,int,const DbgAddr&)),
this, SLOT(slotEnaDisBreak(const QString&,int,const DbgAddr&)));
connect(m_debugger, SIGNAL(activateFileLine(const QString&,int,const DbgAddr&)),
m_filesWindow, SLOT(activate(const QString&,int,const DbgAddr&)));
connect(m_debugger, SIGNAL(executableUpdated()),
m_filesWindow, SLOT(reloadAllFiles()));
connect(m_debugger, SIGNAL(updatePC(const QString&,int,const DbgAddr&,int)),
m_filesWindow, SLOT(updatePC(const QString&,int,const DbgAddr&,int)));
// value popup communication
connect(m_filesWindow, SIGNAL(initiateValuePopup(const QString&)),
m_debugger, SLOT(slotValuePopup(const QString&)));
connect(m_debugger, SIGNAL(valuePopup(const QString&)),
m_filesWindow, SLOT(slotShowValueTip(const QString&)));
// disassembling
connect(m_filesWindow, SIGNAL(disassemble(const QString&, int)),
m_debugger, SLOT(slotDisassemble(const QString&, int)));
connect(m_debugger, SIGNAL(disassembled(const QString&,int,const std::list&)),
m_filesWindow, SLOT(slotDisassembled(const QString&,int,const std::list&)));
connect(m_filesWindow, SIGNAL(moveProgramCounter(const QString&,int,const DbgAddr&)),
m_debugger, SLOT(setProgramCounter(const QString&,int,const DbgAddr&)));
// program stopped
connect(m_debugger, SIGNAL(programStopped()), SLOT(slotProgramStopped()));
connect(&m_backTimer, SIGNAL(timeout()), SLOT(slotBackTimer()));
// tab width
connect(this, SIGNAL(setTabWidth(int)), m_filesWindow, SIGNAL(setTabWidth(int)));
// connect breakpoint table
connect(m_bpTable, SIGNAL(activateFileLine(const QString&,int,const DbgAddr&)),
m_filesWindow, SLOT(activate(const QString&,int,const DbgAddr&)));
connect(m_debugger, SIGNAL(updateUI()), m_bpTable, SLOT(updateUI()));
connect(m_debugger, SIGNAL(breakpointsChanged()), m_bpTable, SLOT(updateBreakList()));
connect(m_debugger, SIGNAL(breakpointsChanged()), m_bpTable, SLOT(updateUI()));
connect(m_debugger, SIGNAL(registersChanged(const std::list&)),
m_registers, SLOT(updateRegisters(const std::list&)));
connect(m_debugger, SIGNAL(memoryDumpChanged(const QString&, const std::list&)),
m_memoryWindow, SLOT(slotNewMemoryDump(const QString&, const std::list&)));
connect(m_debugger, SIGNAL(saveProgramSpecific(KConfigBase*)),
m_memoryWindow, SLOT(saveProgramSpecific(KConfigBase*)));
connect(m_debugger, SIGNAL(restoreProgramSpecific(KConfigBase*)),
m_memoryWindow, SLOT(restoreProgramSpecific(KConfigBase*)));
// thread window
connect(m_debugger, SIGNAL(threadsChanged(const std::list&)),
m_threads, SLOT(updateThreads(const std::list&)));
connect(m_threads, SIGNAL(setThread(int)),
m_debugger, SLOT(setThread(int)));
// popup menu of the local variables window
m_localVariables->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_localVariables, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(slotLocalsPopup(const QPoint&)));
makeDefaultLayout();
setupGUI(KXmlGuiWindow::Default, "kdbgui.rc");
restoreSettings(KGlobal::config());
// The animation button is not part of the restored window state.
// We must create it after the toolbar was loaded.
initAnimation();
updateUI();
m_bpTable->updateUI();
}
DebuggerMainWnd::~DebuggerMainWnd()
{
saveSettings(KGlobal::config());
// must delete m_debugger early since it references our windows
delete m_debugger;
m_debugger = 0;
delete m_memoryWindow;
delete m_threads;
delete m_ttyWindow;
delete m_bpTable;
delete m_registers;
delete m_watches;
delete m_localVariables;
delete m_btWindow;
delete m_filesWindow;
delete m_outputTermProc;
}
QDockWidget* DebuggerMainWnd::createDockWidget(const char* name, const QString& title)
{
QDockWidget* w = new QDockWidget(title, this);
w->setObjectName(name);
// view menu changes when docking state changes
connect(w, SIGNAL(visibilityChanged(bool)), SLOT(updateUI()));
return w;
}
QAction* DebuggerMainWnd::createAction(const QString& text, const char* icon,
int shortcut, const QObject* receiver,
const char* slot, const char* name)
{
KAction* a = actionCollection()->addAction(name);
a->setText(text);
a->setIcon(KIcon(icon));
if (shortcut)
a->setShortcut(KShortcut(shortcut));
connect(a, SIGNAL(triggered()), receiver, slot);
return a;
}
QAction* DebuggerMainWnd::createAction(const QString& text,
int shortcut, const QObject* receiver,
const char* slot, const char* name)
{
KAction* a = actionCollection()->addAction(name);
a->setText(text);
if (shortcut)
a->setShortcut(KShortcut(shortcut));
connect(a, SIGNAL(triggered()), receiver, slot);
return a;
}
void DebuggerMainWnd::initKAction()
{
// file menu
KAction* open = KStandardAction::open(this, SLOT(slotFileOpen()),
actionCollection());
open->setText(i18n("&Open Source..."));
m_closeAction = KStandardAction::close(m_filesWindow, SLOT(slotClose()), actionCollection());
m_reloadAction = createAction(i18n("&Reload Source"), "view-refresh", 0,
m_filesWindow, SLOT(slotFileReload()), "file_reload");
m_fileExecAction = createAction(i18n("&Executable..."),
"document-open-executable", 0,
this, SLOT(slotFileExe()), "file_executable");
m_recentExecAction = KStandardAction::openRecent(this, SLOT(slotRecentExec(const KUrl&)),
actionCollection());
m_recentExecAction->setObjectName("file_executable_recent");
m_recentExecAction->setText(i18n("Recent E&xecutables"));
m_coreDumpAction = createAction(i18n("&Core dump..."), 0,
this, SLOT(slotFileCore()), "file_core_dump");
KStandardAction::quit(kapp, SLOT(closeAllWindows()), actionCollection());
// settings menu
m_settingsAction = createAction(i18n("This &Program..."), 0,
this, SLOT(slotFileProgSettings()), "settings_program");
createAction(i18n("&Global Options..."), 0,
this, SLOT(slotFileGlobalSettings()), "settings_global");
KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actionCollection());
KStandardAction::showStatusbar(this, SLOT(slotViewStatusbar()), actionCollection());
// view menu
m_findAction = KStandardAction::find(m_filesWindow, SLOT(slotViewFind()), actionCollection());
KStandardAction::findNext(m_filesWindow, SLOT(slotFindForward()), actionCollection());
KStandardAction::findPrev(m_filesWindow, SLOT(slotFindBackward()), actionCollection());
i18n("Source &code");
struct { QString text; QWidget* w; QString id; QAction** act; } dw[] = {
{ i18n("Stac&k"), m_btWindow, "view_stack", &m_btWindowAction },
{ i18n("&Locals"), m_localVariables, "view_locals", &m_localVariablesAction },
{ i18n("&Watched expressions"), m_watches, "view_watched_expressions", &m_watchesAction },
{ i18n("&Registers"), m_registers, "view_registers", &m_registersAction },
{ i18n("&Breakpoints"), m_bpTable, "view_breakpoints", &m_bpTableAction },
{ i18n("T&hreads"), m_threads, "view_threads", &m_threadsAction },
{ i18n("&Output"), m_ttyWindow, "view_output", &m_ttyWindowAction },
{ i18n("&Memory"), m_memoryWindow, "view_memory", &m_memoryWindowAction }
};
for (unsigned i = 0; i < sizeof(dw)/sizeof(dw[0]); i++) {
QDockWidget* d = dockParent(dw[i].w);
*dw[i].act = new KToggleAction(dw[i].text, actionCollection());
actionCollection()->addAction(dw[i].id, *dw[i].act);
connect(*dw[i].act, SIGNAL(triggered()), d, SLOT(show()));
}
// execution menu
m_runAction = createAction(i18n("&Run"),
"debug-run", Qt::Key_F5,
m_debugger, SLOT(programRun()), "exec_run");
connect(m_runAction, SIGNAL(activated()), this, SLOT(intoBackground()));
m_stepIntoAction = createAction(i18n("Step &into"),
"debug-step-into", Qt::Key_F8,
m_debugger, SLOT(programStep()), "exec_step_into");
connect(m_stepIntoAction, SIGNAL(activated()), this, SLOT(intoBackground()));
m_stepOverAction = createAction(i18n("Step &over"),
"debug-step-over", Qt::Key_F10,
m_debugger, SLOT(programNext()), "exec_step_over");
connect(m_stepOverAction, SIGNAL(activated()), this, SLOT(intoBackground()));
m_stepOutAction = createAction(i18n("Step o&ut"),
"debug-step-out", Qt::Key_F6,
m_debugger, SLOT(programFinish()), "exec_step_out");
connect(m_stepOutAction, SIGNAL(activated()), this, SLOT(intoBackground()));
m_toCursorAction = createAction(i18n("Run to &cursor"),
"debug-execute-to-cursor", Qt::Key_F7,
this, SLOT(slotExecUntil()), "exec_run_to_cursor");
connect(m_toCursorAction, SIGNAL(activated()), this, SLOT(intoBackground()));
m_stepIntoIAction = createAction(i18n("Step i&nto by instruction"),
"debug-step-into-instruction", Qt::SHIFT+Qt::Key_F8,
m_debugger, SLOT(programStepi()), "exec_step_into_by_insn");
connect(m_stepIntoIAction, SIGNAL(activated()), this, SLOT(intoBackground()));
m_stepOverIAction = createAction(i18n("Step o&ver by instruction"),
"debug-step-instruction", Qt::SHIFT+Qt::Key_F10,
m_debugger, SLOT(programNexti()), "exec_step_over_by_insn");
connect(m_stepOverIAction, SIGNAL(activated()), this, SLOT(intoBackground()));
m_execMovePCAction = createAction(i18n("&Program counter to current line"),
"debug-run-cursor", 0,
m_filesWindow, SLOT(slotMoveProgramCounter()), "exec_movepc");
m_breakAction = createAction(i18n("&Break"), 0,
m_debugger, SLOT(programBreak()), "exec_break");
m_killAction = createAction(i18n("&Kill"), 0,
m_debugger, SLOT(programKill()), "exec_kill");
m_restartAction = createAction(i18n("Re&start"), 0,
m_debugger, SLOT(programRunAgain()), "exec_restart");
m_attachAction = createAction(i18n("A&ttach..."), 0,
this, SLOT(slotExecAttach()), "exec_attach");
m_argumentsAction = createAction(i18n("&Arguments..."), 0,
this, SLOT(slotExecArgs()), "exec_arguments");
// breakpoint menu
m_bpSetAction = createAction(i18n("Set/Clear &breakpoint"), "brkpt", Qt::Key_F9,
m_filesWindow, SLOT(slotBrkptSet()), "breakpoint_set");
m_bpSetTempAction = createAction(i18n("Set &temporary breakpoint"), Qt::SHIFT+Qt::Key_F9,
m_filesWindow, SLOT(slotBrkptSetTemp()), "breakpoint_set_temporary");
m_bpEnableAction = createAction(i18n("&Enable/Disable breakpoint"), Qt::CTRL+Qt::Key_F9,
m_filesWindow, SLOT(slotBrkptEnable()), "breakpoint_enable");
// only in popup menus
createAction(i18n("Watch Expression"), 0,
this, SLOT(slotLocalsToWatch()), "watch_expression");
m_editValueAction = createAction(i18n("Edit Value"), Qt::Key_F2,
this, SLOT(slotEditValue()), "edit_value");
// all actions force an UI update
QList actions = actionCollection()->actions();
foreach(QAction* action, actions) {
connect(action, SIGNAL(activated()), this, SLOT(updateUI()));
}
}
void DebuggerMainWnd::initAnimation()
{
KToolBar* toolbar = toolBar("mainToolBar");
m_animation = new KAnimatedButton(toolbar);
toolbar->addWidget(m_animation);
m_animation->setIcons("pulse");
connect(m_animation, SIGNAL(clicked(bool)), m_debugger, SLOT(programBreak()));
m_animRunning = false;
}
void DebuggerMainWnd::initStatusBar()
{
KStatusBar* statusbar = statusBar();
statusbar->insertItem(m_statusActive, ID_STATUS_ACTIVE);
m_lastActiveStatusText = m_statusActive;
statusbar->insertItem("", ID_STATUS_MSG); /* message pane */
// reserve some translations
i18n("Restart");
i18n("Core dump");
}
bool DebuggerMainWnd::queryClose()
{
if (m_debugger != 0) {
m_debugger->shutdown();
}
return true;
}
// instance properties
void DebuggerMainWnd::saveProperties(KConfigGroup& cg)
{
// session management
QString executable = "";
if (m_debugger != 0) {
executable = m_debugger->executable();
}
cg.writeEntry("executable", executable);
}
void DebuggerMainWnd::readProperties(const KConfigGroup& cg)
{
// session management
QString execName = cg.readEntry("executable");
TRACE("readProperties: executable=" + execName);
if (!execName.isEmpty()) {
debugProgram(execName, "");
}
}
static const char RecentExecutables[] = "RecentExecutables";
static const char LastSession[] = "LastSession";
static const char OutputWindowGroup[] = "OutputWindow";
static const char TermCmdStr[] = "TermCmdStr";
static const char KeepScript[] = "KeepScript";
static const char DebuggerGroup[] = "Debugger";
static const char DebuggerCmdStr[] = "DebuggerCmdStr";
static const char PreferencesGroup[] = "Preferences";
static const char PopForeground[] = "PopForeground";
static const char BackTimeout[] = "BackTimeout";
static const char TabWidth[] = "TabWidth";
static const char SourceFileFilter[] = "SourceFileFilter";
static const char HeaderFileFilter[] = "HeaderFileFilter";
void DebuggerMainWnd::saveSettings(KSharedConfigPtr config)
{
m_recentExecAction->saveEntries(config->group(RecentExecutables));
KConfigGroup lg = config->group(LastSession);
lg.writeEntry("Width0Locals", m_localVariables->columnWidth(0));
lg.writeEntry("Width0Watches", m_watches->columnWidth(0));
if (m_debugger != 0) {
m_debugger->saveSettings(config.data());
}
config->group(OutputWindowGroup).writeEntry(TermCmdStr, m_outputTermCmdStr);
config->group(DebuggerGroup).writeEntry(DebuggerCmdStr, m_debuggerCmdStr);
KConfigGroup pg(config->group(PreferencesGroup));
pg.writeEntry(PopForeground, m_popForeground);
pg.writeEntry(BackTimeout, m_backTimeout);
pg.writeEntry(TabWidth, m_tabWidth);
pg.writeEntry(SourceFileFilter, m_sourceFilter);
pg.writeEntry(HeaderFileFilter, m_headerFilter);
}
void DebuggerMainWnd::restoreSettings(KSharedConfigPtr config)
{
m_recentExecAction->loadEntries(config->group(RecentExecutables));
KConfigGroup lg = config->group(LastSession);
int w;
w = lg.readEntry("Width0Locals", -1);
if (w >= 0 && w < 30000)
m_localVariables->setColumnWidth(0, w);
w = lg.readEntry("Width0Watches", -1);
if (w >= 0 && w < 30000)
m_watches->setColumnWidth(0, w);
if (m_debugger != 0) {
m_debugger->restoreSettings(config.data());
}
KConfigGroup og(config->group(OutputWindowGroup));
/*
* For debugging and emergency purposes, let the config file override
* the shell script that is used to keep the output window open. This
* string must have EXACTLY 1 %s sequence in it.
*/
setTerminalCmd(og.readEntry(TermCmdStr, defaultTermCmdStr));
m_outputTermKeepScript = og.readEntry(KeepScript);
setDebuggerCmdStr(config->group(DebuggerGroup).readEntry(DebuggerCmdStr));
KConfigGroup pg(config->group(PreferencesGroup));
m_popForeground = pg.readEntry(PopForeground, false);
m_backTimeout = pg.readEntry(BackTimeout, 1000);
m_tabWidth = pg.readEntry(TabWidth, 0);
m_sourceFilter = pg.readEntry(SourceFileFilter, m_sourceFilter);
m_headerFilter = pg.readEntry(HeaderFileFilter, m_headerFilter);
emit setTabWidth(m_tabWidth);
}
void DebuggerMainWnd::updateUI()
{
m_findAction->setChecked(m_filesWindow->m_findDlg.isVisible());
m_findAction->setEnabled(m_filesWindow->hasWindows());
m_bpSetAction->setEnabled(m_debugger->canChangeBreakpoints());
m_bpSetTempAction->setEnabled(m_debugger->canChangeBreakpoints());
m_bpEnableAction->setEnabled(m_debugger->canChangeBreakpoints());
m_bpTableAction->setChecked(isDockVisible(m_bpTable));
m_btWindowAction->setChecked(isDockVisible(m_btWindow));
m_localVariablesAction->setChecked(isDockVisible(m_localVariables));
m_watchesAction->setChecked(isDockVisible(m_watches));
m_registersAction->setChecked(isDockVisible(m_registers));
m_threadsAction->setChecked(isDockVisible(m_threads));
m_memoryWindowAction->setChecked(isDockVisible(m_memoryWindow));
m_ttyWindowAction->setChecked(isDockVisible(m_ttyWindow));
m_fileExecAction->setEnabled(m_debugger->isIdle());
m_settingsAction->setEnabled(m_debugger->haveExecutable());
m_coreDumpAction->setEnabled(m_debugger->canStart());
m_closeAction->setEnabled(m_filesWindow->hasWindows());
m_reloadAction->setEnabled(m_filesWindow->hasWindows());
m_stepIntoAction->setEnabled(m_debugger->canSingleStep());
m_stepIntoIAction->setEnabled(m_debugger->canSingleStep());
m_stepOverAction->setEnabled(m_debugger->canSingleStep());
m_stepOverIAction->setEnabled(m_debugger->canSingleStep());
m_stepOutAction->setEnabled(m_debugger->canSingleStep());
m_toCursorAction->setEnabled(m_debugger->canSingleStep());
m_execMovePCAction->setEnabled(m_debugger->canSingleStep());
m_restartAction->setEnabled(m_debugger->canSingleStep());
m_attachAction->setEnabled(m_debugger->isReady());
m_runAction->setEnabled(m_debugger->canStart() || m_debugger->canSingleStep());
m_killAction->setEnabled(m_debugger->haveExecutable() && m_debugger->isProgramActive());
m_breakAction->setEnabled(m_debugger->isProgramRunning());
m_argumentsAction->setEnabled(m_debugger->haveExecutable());
m_editValueAction->setEnabled(m_debugger->canSingleStep());
// animation
if (m_debugger->isIdle()) {
if (m_animRunning && m_animation) {
m_animation->stop();
m_animRunning = false;
}
} else {
if (!m_animRunning && m_animation) {
m_animation->start();
m_animRunning = true;
}
}
// update statusbar
QString newStatus;
if (m_debugger->isProgramActive())
newStatus = m_statusActive;
if (newStatus != m_lastActiveStatusText) {
statusBar()->changeItem(newStatus, ID_STATUS_ACTIVE);
m_lastActiveStatusText = newStatus;
}
}
void DebuggerMainWnd::updateLineItems()
{
m_filesWindow->updateLineItems(m_debugger);
}
void DebuggerMainWnd::slotAddWatch()
{
if (m_debugger != 0) {
QString t = m_watches->watchText();
m_debugger->addWatch(t);
}
}
void DebuggerMainWnd::slotAddWatch(const QString& text)
{
if (m_debugger != 0) {
m_debugger->addWatch(text);
}
}
void DebuggerMainWnd::slotNewFileLoaded()
{
// updates program counter in the new file
if (m_debugger != 0)
m_filesWindow->updateLineItems(m_debugger);
}
QDockWidget* DebuggerMainWnd::dockParent(QWidget* w)
{
while ((w = w->parentWidget()) != 0) {
if (QDockWidget* dock = qobject_cast(w))
return dock;
}
return 0;
}
bool DebuggerMainWnd::isDockVisible(QWidget* w)
{
QDockWidget* d = dockParent(w);
return d != 0 && d->isVisible();
}
void DebuggerMainWnd::makeDefaultLayout()
{
// +---------------+---------+
// | Source | Locals |
// | | |
// |---------------+---------+
// |Stack, Brkpts, | Watches |
// |Output,... | |
// +---------------+---------+
addDockWidget(Qt::RightDockWidgetArea, dockParent(m_localVariables));
addDockWidget(Qt::BottomDockWidgetArea, dockParent(m_memoryWindow));
splitDockWidget(dockParent(m_memoryWindow), dockParent(m_threads), Qt::Horizontal);
tabifyDockWidget(dockParent(m_memoryWindow), dockParent(m_registers));
tabifyDockWidget(dockParent(m_registers), dockParent(m_bpTable));
tabifyDockWidget(dockParent(m_bpTable), dockParent(m_ttyWindow));
tabifyDockWidget(dockParent(m_ttyWindow), dockParent(m_btWindow));
tabifyDockWidget(dockParent(m_threads), dockParent(m_watches));
dockParent(m_localVariables)->setVisible(true);
dockParent(m_ttyWindow)->setVisible(true);
dockParent(m_watches)->setVisible(true);
dockParent(m_btWindow)->setVisible(true);
dockParent(m_bpTable)->setVisible(true);
}
bool DebuggerMainWnd::debugProgram(const QString& exe, const QString& lang)
{
// check the file name
QFileInfo fi(exe);
bool success = fi.isFile();
if (!success)
{
QString msg = i18n("`%1' is not a file or does not exist");
KMessageBox::sorry(this, msg.arg(exe));
}
else
{
success = startDriver(fi.absoluteFilePath(), lang);
}
if (success)
{
m_recentExecAction->addUrl(KUrl(fi.absoluteFilePath()));
// keep the directory
m_lastDirectory = fi.absolutePath();
m_filesWindow->setExtraDirectory(m_lastDirectory);
// set caption to basename part of executable
QString caption = fi.fileName();
setCaption(caption);
}
else
{
m_recentExecAction->removeUrl(KUrl(fi.absoluteFilePath()));
}
return success;
}
static const char GeneralGroup[] = "General";
bool DebuggerMainWnd::startDriver(const QString& executable, QString lang)
{
assert(m_debugger != 0);
TRACE(QString("trying language '%1'...").arg(lang));
DebuggerDriver* driver = driverFromLang(lang);
if (driver == 0)
{
// see if there is a language in the per-program config file
QString configName = m_debugger->getConfigForExe(executable);
if (QFile::exists(configName))
{
KConfig c(configName, KConfig::SimpleConfig);
// Using "GDB" as default here is for backwards compatibility:
// The config file exists but doesn't have an entry,
// so it must have been created by an old version of KDbg
// that had only the GDB driver.
lang = c.group(GeneralGroup)
.readEntry(KDebugger::DriverNameEntry, "GDB");
TRACE(QString("...bad, trying config driver %1...").arg(lang));
driver = driverFromLang(lang);
}
}
if (driver == 0)
{
QString name = driverNameFromFile(executable);
TRACE(QString("...no luck, trying %1 derived"
" from file contents").arg(name));
driver = driverFromLang(name);
}
if (driver == 0)
{
// oops
QString msg = i18n("Don't know how to debug language `%1'");
KMessageBox::sorry(this, msg.arg(lang));
return false;
}
driver->setLogFileName(m_transcriptFile);
bool success = m_debugger->debugProgram(executable, driver);
if (!success)
{
delete driver;
QString msg = i18n("Could not start the debugger process.\n"
"Please shut down KDbg and resolve the problem.");
KMessageBox::sorry(this, msg);
}
return success;
}
// derive driver from language
DebuggerDriver* DebuggerMainWnd::driverFromLang(QString lang)
{
// lang is needed in all lowercase
lang = lang.toLower();
// The following table relates languages and debugger drivers
static const struct L {
const char* shortest; // abbreviated to this is still unique
const char* full; // full name of language
int driver;
} langs[] = {
{ "c", "c++", 1 },
{ "f", "fortran", 1 },
{ "p", "python", 3 },
{ "x", "xslt", 2 },
// the following are actually driver names
{ "gdb", "gdb", 1 },
{ "xsldbg", "xsldbg", 2 },
};
const int N = sizeof(langs)/sizeof(langs[0]);
// lookup the language name
int driverID = 0;
for (int i = 0; i < N; i++)
{
const L& l = langs[i];
// shortest must match
if (!lang.startsWith(l.shortest))
continue;
// lang must not be longer than the full name, and it must match
if (QString(l.full).startsWith(lang))
{
driverID = l.driver;
break;
}
}
DebuggerDriver* driver = 0;
switch (driverID) {
case 1:
{
GdbDriver* gdb = new GdbDriver;
gdb->setDefaultInvocation(m_debuggerCmdStr);
driver = gdb;
}
break;
case 2:
driver = new XsldbgDriver;
break;
default:
// unknown language
break;
}
return driver;
}
/**
* Try to guess the language to use from the contents of the file.
*/
QString DebuggerMainWnd::driverNameFromFile(const QString& exe)
{
/* Inprecise but simple test to see if file is in XSLT language */
if (exe.right(4).toLower() == ".xsl")
return "XSLT";
return "GDB";
}
void DebuggerMainWnd::setCoreFile(const QString& corefile)
{
assert(m_debugger != 0);
m_debugger->useCoreFile(corefile, true);
}
void DebuggerMainWnd::setRemoteDevice(const QString& remoteDevice)
{
if (m_debugger != 0) {
m_debugger->setRemoteDevice(remoteDevice);
}
}
void DebuggerMainWnd::overrideProgramArguments(const QString& args)
{
assert(m_debugger != 0);
m_debugger->overrideProgramArguments(args);
}
void DebuggerMainWnd::setTranscript(const QString& name)
{
m_transcriptFile = name;
if (m_debugger != 0 && m_debugger->driver() != 0)
m_debugger->driver()->setLogFileName(m_transcriptFile);
}
void DebuggerMainWnd::setAttachPid(const QString& pid)
{
assert(m_debugger != 0);
m_debugger->setAttachPid(pid);
}
void DebuggerMainWnd::slotNewStatusMsg()
{
QString msg = m_debugger->statusMessage();
statusBar()->changeItem(msg, ID_STATUS_MSG);
}
void DebuggerMainWnd::slotFileGlobalSettings()
{
int oldTabWidth = m_tabWidth;
KPageDialog dlg(this);
QString title = KGlobal::caption();
title += i18n(": Global options");
dlg.setCaption(title);
PrefDebugger prefDebugger(&dlg);
prefDebugger.setDebuggerCmd(m_debuggerCmdStr.isEmpty() ?
GdbDriver::defaultGdb() : m_debuggerCmdStr);
prefDebugger.setTerminal(m_outputTermCmdStr);
PrefMisc prefMisc(&dlg);
prefMisc.setPopIntoForeground(m_popForeground);
prefMisc.setBackTimeout(m_backTimeout);
prefMisc.setTabWidth(m_tabWidth);
prefMisc.setSourceFilter(m_sourceFilter);
prefMisc.setHeaderFilter(m_headerFilter);
dlg.addPage(&prefDebugger, i18n("Debugger"));
dlg.addPage(&prefMisc, i18n("Miscellaneous"));
if (dlg.exec() == QDialog::Accepted)
{
setDebuggerCmdStr(prefDebugger.debuggerCmd());
setTerminalCmd(prefDebugger.terminal());
m_popForeground = prefMisc.popIntoForeground();
m_backTimeout = prefMisc.backTimeout();
m_tabWidth = prefMisc.tabWidth();
m_sourceFilter = prefMisc.sourceFilter();
if (m_sourceFilter.isEmpty())
m_sourceFilter = defaultSourceFilter;
m_headerFilter = prefMisc.headerFilter();
if (m_headerFilter.isEmpty())
m_headerFilter = defaultHeaderFilter;
}
if (m_tabWidth != oldTabWidth) {
emit setTabWidth(m_tabWidth);
}
}
void DebuggerMainWnd::setTerminalCmd(const QString& cmd)
{
m_outputTermCmdStr = cmd;
// revert to default if empty
if (m_outputTermCmdStr.isEmpty()) {
m_outputTermCmdStr = defaultTermCmdStr;
}
}
void DebuggerMainWnd::setDebuggerCmdStr(const QString& cmd)
{
m_debuggerCmdStr = cmd;
// make empty if it is the default
if (m_debuggerCmdStr == GdbDriver::defaultGdb()) {
m_debuggerCmdStr = QString();
}
}
void DebuggerMainWnd::slotDebuggerStarting()
{
if (m_debugger == 0) /* paranoia check */
return;
if (m_ttyLevel == m_debugger->ttyLevel())
return;
// shut down terminal emulations we will not need
switch (m_ttyLevel) {
case KDebugger::ttySimpleOutputOnly:
m_ttyWindow->deactivate();
break;
case KDebugger::ttyFull:
m_outputTermProc->kill();
break;
default: break;
}
m_ttyLevel = m_debugger->ttyLevel();
QString ttyName;
switch (m_ttyLevel) {
case KDebugger::ttySimpleOutputOnly:
ttyName = m_ttyWindow->activate();
break;
case KDebugger::ttyFull:
// create an output window
ttyName = createOutputWindow();
TRACE(ttyName.isEmpty() ?
"createOuputWindow failed" : "successfully created output window");
break;
default: break;
}
m_debugger->setTerminal(ttyName);
}
void DebuggerMainWnd::slotToggleBreak(const QString& fileName, int lineNo,
const DbgAddr& address, bool temp)
{
// lineNo is zero-based
if (m_debugger != 0) {
m_debugger->setBreakpoint(fileName, lineNo, address, temp);
}
}
void DebuggerMainWnd::slotEnaDisBreak(const QString& fileName, int lineNo,
const DbgAddr& address)
{
// lineNo is zero-based
if (m_debugger != 0) {
m_debugger->enableDisableBreakpoint(fileName, lineNo, address);
}
}
QString DebuggerMainWnd::createOutputWindow()
{
// create a name for a fifo
QString fifoName;
fifoName.sprintf("/tmp/kdbgttywin%05d", ::getpid());
// create a fifo that will pass in the tty name
QFile::remove(fifoName); // remove remnants
#ifdef HAVE_MKFIFO
if (::mkfifo(fifoName.toLocal8Bit(), S_IRUSR|S_IWUSR) < 0) {
// failed
TRACE("mkfifo " + fifoName + " failed");
return QString();
}
#else
if (::mknod(fifoName.toLocal8Bit(), S_IFIFO | S_IRUSR|S_IWUSR, 0) < 0) {
// failed
TRACE("mknod " + fifoName + " failed");
return QString();
}
#endif
/*
* Spawn an xterm that in turn runs a shell script that passes us
* back the terminal name and then only sits and waits.
*/
static const char shellScriptFmt[] =
"tty>%s;"
"trap \"\" INT QUIT TSTP;" /* ignore various signals */
"exec<&-;exec>&-;" /* close stdin and stdout */
"while :;do sleep 3600;done";
// let config file override this script
QString shellScript;
if (!m_outputTermKeepScript.isEmpty()) {
shellScript = m_outputTermKeepScript;
} else {
shellScript = shellScriptFmt;
}
shellScript.replace("%s", fifoName);
TRACE("output window script is " + shellScript);
QString title = KGlobal::caption();
title += i18n(": Program output");
// parse the command line specified in the preferences
QStringList cmdParts = m_outputTermCmdStr.split(' ');
/*
* Build the argv array. Thereby substitute special sequences:
*/
struct {
char seq[4];
QString replace;
} substitute[] = {
{ "%T", title },
{ "%C", shellScript }
};
for (QStringList::iterator i = cmdParts.begin(); i != cmdParts.end(); ++i)
{
QString& str = *i;
for (int j = sizeof(substitute)/sizeof(substitute[0])-1; j >= 0; j--) {
int pos = str.indexOf(substitute[j].seq);
if (pos >= 0) {
str.replace(pos, 2, substitute[j].replace);
break; /* substitute only one sequence */
}
}
}
QString tty, pgm = cmdParts.takeFirst();
m_outputTermProc->start(pgm, cmdParts);
if (m_outputTermProc->waitForStarted())
{
// read the ttyname from the fifo
QFile f(fifoName);
if (f.open(QIODevice::ReadOnly))
{
QByteArray t = f.readAll();
tty = QString::fromLocal8Bit(t, t.size());
f.close();
}
f.remove();
// remove whitespace
tty = tty.trimmed();
TRACE("tty=" + tty);
}
else
{
// error, could not start xterm
TRACE("fork failed for fifo " + fifoName);
QFile::remove(fifoName);
}
return tty;
}
void DebuggerMainWnd::slotProgramStopped()
{
// when the program stopped, move the window to the foreground
if (m_popForeground) {
// unfortunately, this requires quite some force to work :-(
KWindowSystem::raiseWindow(winId());
KWindowSystem::forceActiveWindow(winId());
}
m_backTimer.stop();
}
void DebuggerMainWnd::intoBackground()
{
if (m_popForeground) {
m_backTimer.setSingleShot(true);
m_backTimer.start(m_backTimeout);
}
}
void DebuggerMainWnd::slotBackTimer()
{
lower();
}
void DebuggerMainWnd::slotRecentExec(const KUrl& url)
{
QString exe = url.path();
debugProgram(exe, "");
}
QString DebuggerMainWnd::makeSourceFilter()
{
QString f;
f = m_sourceFilter + " " + m_headerFilter + i18n("|All source files\n");
f += m_sourceFilter + i18n("|Source files\n");
f += m_headerFilter + i18n("|Header files\n");
f += i18n("*|All files");
return f;
}
/*
* Pop up the context menu in the locals window
*/
void DebuggerMainWnd::slotLocalsPopup(const QPoint& pt)
{
QMenu* popup = static_cast(factory()->container("popup_locals", this));
if (popup == 0) {
return;
}
if (popup->isVisible()) {
popup->hide();
} else {
popup->popup(m_localVariables->viewport()->mapToGlobal(pt));
}
}
/*
* Copies the currently selected item to the watch window.
*/
void DebuggerMainWnd::slotLocalsToWatch()
{
VarTree* item = m_localVariables->selectedItem();
if (item != 0 && m_debugger != 0) {
QString text = item->computeExpr();
m_debugger->addWatch(text);
}
}
/*
* Starts editing a value in a value display
*/
void DebuggerMainWnd::slotEditValue()
{
// does one of the value trees have the focus
QWidget* f = kapp->focusWidget();
ExprWnd* wnd;
if (f == m_localVariables) {
wnd = m_localVariables;
} else if (f == m_watches->watchVariables()) {
wnd = m_watches->watchVariables();
} else {
return;
}
if (m_localVariables->isEditing() ||
m_watches->watchVariables()->isEditing())
{
return; /* don't edit twice */
}
VarTree* expr = wnd->selectedItem();
if (expr != 0 && m_debugger != 0 && m_debugger->canSingleStep())
{
TRACE("edit value");
// determine the text to edit
QString text = m_debugger->driver()->editableValue(expr);
wnd->editValue(expr, text);
}
}
// helper that gets a file name (it only differs in the caption of the dialog)
static QString myGetFileName(QString caption,
QString dir, QString filter,
QWidget* parent)
{
QString filename;
KFileDialog dlg(dir, filter, parent);
dlg.setCaption(caption);
if (dlg.exec() == QDialog::Accepted)
filename = dlg.selectedFile();
return filename;
}
void DebuggerMainWnd::slotFileOpen()
{
// start browsing in the active file's directory
// fall back to last used directory (executable)
QString dir = m_lastDirectory;
QString fileName = m_filesWindow->activeFileName();
if (!fileName.isEmpty()) {
QFileInfo fi(fileName);
dir = fi.path();
}
fileName = myGetFileName(i18n("Open"),
dir,
makeSourceFilter(), this);
if (!fileName.isEmpty())
{
QFileInfo fi(fileName);
m_lastDirectory = fi.path();
m_filesWindow->setExtraDirectory(m_lastDirectory);
m_filesWindow->activateFile(fileName);
}
}
void DebuggerMainWnd::slotFileExe()
{
if (m_debugger->isIdle())
{
// open a new executable
QString executable = myGetFileName(i18n("Select the executable to debug"),
m_lastDirectory, 0, this);
if (executable.isEmpty())
return;
debugProgram(executable, "");
}
}
void DebuggerMainWnd::slotFileCore()
{
if (m_debugger->canStart())
{
QString corefile = myGetFileName(i18n("Select core dump"),
m_lastDirectory, 0, this);
if (!corefile.isEmpty()) {
m_debugger->useCoreFile(corefile, false);
}
}
}
void DebuggerMainWnd::slotFileProgSettings()
{
if (m_debugger != 0) {
m_debugger->programSettings(this);
}
}
void DebuggerMainWnd::slotViewStatusbar()
{
if (statusBar()->isVisible())
statusBar()->hide();
else
statusBar()->show();
setSettingsDirty();
}
void DebuggerMainWnd::slotExecUntil()
{
if (m_debugger != 0)
{
QString file;
int lineNo;
if (m_filesWindow->activeLine(file, lineNo))
m_debugger->runUntil(file, lineNo);
}
}
void DebuggerMainWnd::slotExecAttach()
{
#ifdef PS_COMMAND
ProcAttachPS dlg(this);
// seed filter with executable name
QFileInfo fi = m_debugger->executable();
dlg.setFilterText(fi.fileName());
#else
ProcAttach dlg(this);
dlg.setText(m_debugger->attachedPid());
#endif
if (dlg.exec()) {
m_debugger->attachProgram(dlg.text());
}
}
void DebuggerMainWnd::slotExecArgs()
{
if (m_debugger != 0) {
m_debugger->programArgs(this);
}
}
void DebuggerMainWnd::slotConfigureKeys()
{
KShortcutsDialog::configure(actionCollection());
}
#include "dbgmainwnd.moc"
kdbg-2.5.4/kdbg/dbgmainwnd.h 0000664 0000000 0000000 00000013152 12237506721 0015632 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#ifndef DBGMAINWND_H
#define DBGMAINWND_H
#include
#include
#include "regwnd.h"
class QDockWidget;
class QProcess;
class KAnimatedButton;
class KRecentFilesAction;
class KUrl;
class WinStack;
class QListWidget;
class ExprWnd;
class BreakpointTable;
class ThreadList;
class MemoryWindow;
class TTYWindow;
class WatchWindow;
class KDebugger;
class DebuggerDriver;
struct DbgAddr;
class DebuggerMainWnd : public KXmlGuiWindow
{
Q_OBJECT
public:
DebuggerMainWnd();
~DebuggerMainWnd();
bool debugProgram(const QString& exe, const QString& lang);
/**
* Specifies the file where to write the transcript.
*/
void setTranscript(const QString& name);
/**
* Specifies the process to attach to after the program is loaded.
*/
void setAttachPid(const QString& pid);
// the following are needed to handle program arguments
void setCoreFile(const QString& corefile);
void setRemoteDevice(const QString &remoteDevice);
void overrideProgramArguments(const QString& args);
protected:
// session properties
virtual void saveProperties(KConfigGroup& cg);
virtual void readProperties(const KConfigGroup& cg);
// settings
void saveSettings(KSharedConfigPtr);
void restoreSettings(KSharedConfigPtr);
void initAnimation();
void initStatusBar();
void initKAction();
// view windows
WinStack* m_filesWindow;
QListWidget* m_btWindow;
ExprWnd* m_localVariables;
WatchWindow* m_watches;
RegisterView* m_registers;
BreakpointTable* m_bpTable;
TTYWindow* m_ttyWindow;
ThreadList* m_threads;
MemoryWindow* m_memoryWindow;
QTimer m_backTimer;
// recent execs in File menu
QAction* m_closeAction;
QAction* m_reloadAction;
QAction* m_fileExecAction;
KRecentFilesAction* m_recentExecAction;
QAction* m_coreDumpAction;
QAction* m_settingsAction;
QAction* m_findAction;
QAction* m_btWindowAction;
QAction* m_localVariablesAction;
QAction* m_watchesAction;
QAction* m_registersAction;
QAction* m_bpTableAction;
QAction* m_ttyWindowAction;
QAction* m_threadsAction;
QAction* m_memoryWindowAction;
QAction* m_runAction;
QAction* m_stepIntoAction;
QAction* m_stepOverAction;
QAction* m_stepOutAction;
QAction* m_toCursorAction;
QAction* m_stepIntoIAction;
QAction* m_stepOverIAction;
QAction* m_execMovePCAction;
QAction* m_breakAction;
QAction* m_killAction;
QAction* m_restartAction;
QAction* m_attachAction;
QAction* m_argumentsAction;
QAction* m_bpSetAction;
QAction* m_bpSetTempAction;
QAction* m_bpEnableAction;
QAction* m_editValueAction;
QString m_lastDirectory; /* the dir of the most recently opened file */
protected:
virtual bool queryClose();
QAction* createAction(const QString& text, const char* icon,
int shortcut, const QObject* receiver,
const char* slot, const char* name);
QAction* createAction(const QString& text,
int shortcut, const QObject* receiver,
const char* slot, const char* name);
// the debugger proper
QString m_debuggerCmdStr;
KDebugger* m_debugger;
QString m_transcriptFile; /* where gdb dialog is logged */
/**
* Starts to debug the specified program using the specified language
* driver.
*/
bool startDriver(const QString& executable, QString lang);
DebuggerDriver* driverFromLang(QString lang);
/**
* Derives a driver name from the contents of the named file.
*/
QString driverNameFromFile(const QString& exe);
// output window
QString m_outputTermCmdStr;
QString m_outputTermKeepScript;
QProcess* m_outputTermProc;
int m_ttyLevel;
QString createOutputWindow();
bool m_popForeground; /* whether main wnd raises when prog stops */
int m_backTimeout; /* when wnd goes back */
int m_tabWidth; /* tab width in characters (can be 0) */
QString m_sourceFilter;
QString m_headerFilter;
void setTerminalCmd(const QString& cmd);
void setDebuggerCmdStr(const QString& cmd);
QDockWidget* createDockWidget(const char* name, const QString& title);
QDockWidget* dockParent(QWidget* w);
bool isDockVisible(QWidget* w);
void makeDefaultLayout();
QString makeSourceFilter();
// to avoid flicker when the status bar is updated,
// we store the last string that we put there
KAnimatedButton* m_animation;
QString m_lastActiveStatusText;
bool m_animRunning;
// statusbar texts
QString m_statusActive;
signals:
void setTabWidth(int tabWidth);
public slots:
virtual void updateUI();
virtual void updateLineItems();
void slotAddWatch();
void slotAddWatch(const QString& text);
void slotNewFileLoaded();
void slotNewStatusMsg();
void slotDebuggerStarting();
void slotToggleBreak(const QString&, int, const DbgAddr&, bool);
void slotEnaDisBreak(const QString&, int, const DbgAddr&);
void slotProgramStopped();
void slotBackTimer();
void slotRecentExec(const KUrl& url);
void slotLocalsPopup(const QPoint& pt);
void slotLocalsToWatch();
void slotEditValue();
void slotFileOpen();
void slotFileExe();
void slotFileCore();
void slotFileGlobalSettings();
void slotFileProgSettings();
void slotViewStatusbar();
void slotExecUntil();
void slotExecAttach();
void slotExecArgs();
void intoBackground();
void slotConfigureKeys();
};
#endif // DBGMAINWND_H
kdbg-2.5.4/kdbg/debugger.cpp 0000664 0000000 0000000 00000161560 12237506721 0015646 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#include "debugger.h"
#include "dbgdriver.h"
#include "pgmargs.h"
#include "typetable.h"
#include "exprwnd.h"
#include "pgmsettings.h"
#include
#include
#include
#include // KMD5
#include
#include /* i18n */
#include
#include
#include
#include /* strtol, atoi */
#include /* sleep(3) */
#include
#include "mydebug.h"
KDebugger::KDebugger(QWidget* parent,
ExprWnd* localVars,
ExprWnd* watchVars,
QListWidget* backtrace) :
QObject(parent),
m_ttyLevel(ttyFull),
m_memoryFormat(MDTword | MDThex),
m_haveExecutable(false),
m_programActive(false),
m_programRunning(false),
m_sharedLibsListed(false),
m_typeTable(0),
m_programConfig(0),
m_d(0),
m_localVariables(*localVars),
m_watchVariables(*watchVars),
m_btWindow(*backtrace)
{
connect(&m_localVariables, SIGNAL(itemExpanded(QTreeWidgetItem*)),
SLOT(slotExpanding(QTreeWidgetItem*)));
connect(&m_watchVariables, SIGNAL(itemExpanded(QTreeWidgetItem*)),
SLOT(slotExpanding(QTreeWidgetItem*)));
connect(&m_localVariables, SIGNAL(editValueCommitted(VarTree*, const QString&)),
SLOT(slotValueEdited(VarTree*, const QString&)));
connect(&m_watchVariables, SIGNAL(editValueCommitted(VarTree*, const QString&)),
SLOT(slotValueEdited(VarTree*, const QString&)));
connect(&m_btWindow, SIGNAL(currentRowChanged(int)), this, SLOT(gotoFrame(int)));
emit updateUI();
}
KDebugger::~KDebugger()
{
if (m_programConfig != 0) {
saveProgramSettings();
m_programConfig->sync();
delete m_programConfig;
}
delete m_typeTable;
}
void KDebugger::saveSettings(KConfig* /*config*/)
{
}
void KDebugger::restoreSettings(KConfig* /*config*/)
{
}
//////////////////////////////////////////////////////////////////////
// external interface
const char GeneralGroup[] = "General";
const char DebuggerCmdStr[] = "DebuggerCmdStr";
const char TTYLevelEntry[] = "TTYLevel";
const char KDebugger::DriverNameEntry[] = "DriverName";
bool KDebugger::debugProgram(const QString& name,
DebuggerDriver* driver)
{
if (m_d != 0 && m_d->isRunning())
{
QApplication::setOverrideCursor(Qt::WaitCursor);
stopDriver();
QApplication::restoreOverrideCursor();
if (m_d->isRunning() || m_haveExecutable) {
/* timed out! We can't really do anything useful now */
TRACE("timed out while waiting for gdb to die!");
return false;
}
delete m_d;
m_d = 0;
}
// wire up the driver
connect(driver, SIGNAL(activateFileLine(const QString&,int,const DbgAddr&)),
this, SIGNAL(activateFileLine(const QString&,int,const DbgAddr&)));
connect(driver, SIGNAL(finished(int, QProcess::ExitStatus)),
SLOT(gdbExited()));
connect(driver, SIGNAL(commandReceived(CmdQueueItem*,const char*)),
SLOT(parse(CmdQueueItem*,const char*)));
connect(driver, SIGNAL(bytesWritten(qint64)), SIGNAL(updateUI()));
connect(driver, SIGNAL(inferiorRunning()), SLOT(slotInferiorRunning()));
connect(driver, SIGNAL(enterIdleState()), SLOT(backgroundUpdate()));
connect(driver, SIGNAL(enterIdleState()), SIGNAL(updateUI()));
connect(&m_localVariables, SIGNAL(removingItem(VarTree*)),
driver, SLOT(dequeueCmdByVar(VarTree*)));
connect(&m_watchVariables, SIGNAL(removingItem(VarTree*)),
driver, SLOT(dequeueCmdByVar(VarTree*)));
// create the program settings object
openProgramConfig(name);
// get debugger command from per-program settings
if (m_programConfig != 0) {
KConfigGroup g = m_programConfig->group(GeneralGroup);
m_debuggerCmd = readDebuggerCmd(g);
// get terminal emulation level
m_ttyLevel = TTYLevel(g.readEntry(TTYLevelEntry, int(ttyFull)));
}
// the rest is read in later in the handler of DCexecutable
m_d = driver;
if (!startDriver()) {
TRACE("startDriver failed");
m_d = 0;
return false;
}
TRACE("before file cmd");
m_d->executeCmd(DCexecutable, name);
m_executable = name;
// set remote target
if (!m_remoteDevice.isEmpty()) {
m_d->executeCmd(DCtargetremote, m_remoteDevice);
m_d->queueCmd(DCbt, DebuggerDriver::QMoverride);
m_d->queueCmd(DCinfothreads, DebuggerDriver::QMoverride);
m_d->queueCmd(DCframe, 0, DebuggerDriver::QMnormal);
m_programActive = true;
m_haveExecutable = true;
}
// create a type table
m_typeTable = new ProgramTypeTable;
m_sharedLibsListed = false;
emit updateUI();
return true;
}
void KDebugger::shutdown()
{
// shut down debugger driver
if (m_d != 0 && m_d->isRunning())
{
stopDriver();
}
}
void KDebugger::useCoreFile(QString corefile, bool batch)
{
m_corefile = corefile;
if (!batch) {
CmdQueueItem* cmd = loadCoreFile();
cmd->m_byUser = true;
}
}
void KDebugger::setAttachPid(const QString& pid)
{
m_attachedPid = pid;
}
void KDebugger::programRun()
{
if (!isReady())
return;
// when program is active, but not a core file, continue
// otherwise run the program
if (m_programActive && m_corefile.isEmpty()) {
// gdb command: continue
m_d->executeCmd(DCcont, true);
} else {
// gdb command: run
m_d->executeCmd(DCrun, true);
m_corefile = QString();
m_programActive = true;
}
m_programRunning = true;
}
void KDebugger::attachProgram(const QString& pid)
{
if (!isReady())
return;
m_attachedPid = pid;
TRACE("Attaching to " + m_attachedPid);
m_d->executeCmd(DCattach, m_attachedPid);
m_programActive = true;
m_programRunning = true;
}
void KDebugger::programRunAgain()
{
if (canSingleStep()) {
m_d->executeCmd(DCrun, true);
m_corefile = QString();
m_programRunning = true;
}
}
void KDebugger::programStep()
{
if (canSingleStep()) {
m_d->executeCmd(DCstep, true);
m_programRunning = true;
}
}
void KDebugger::programNext()
{
if (canSingleStep()) {
m_d->executeCmd(DCnext, true);
m_programRunning = true;
}
}
void KDebugger::programStepi()
{
if (canSingleStep()) {
m_d->executeCmd(DCstepi, true);
m_programRunning = true;
}
}
void KDebugger::programNexti()
{
if (canSingleStep()) {
m_d->executeCmd(DCnexti, true);
m_programRunning = true;
}
}
void KDebugger::programFinish()
{
if (canSingleStep()) {
m_d->executeCmd(DCfinish, true);
m_programRunning = true;
}
}
void KDebugger::programKill()
{
if (haveExecutable() && isProgramActive()) {
if (m_programRunning) {
m_d->interruptInferior();
}
// this is an emergency command; flush queues
m_d->flushCommands(true);
m_d->executeCmd(DCkill, true);
}
}
bool KDebugger::runUntil(const QString& fileName, int lineNo)
{
if (isReady() && m_programActive && !m_programRunning) {
// strip off directory part of file name
QFileInfo fi(fileName);
m_d->executeCmd(DCuntil, fi.fileName(), lineNo, true);
m_programRunning = true;
return true;
} else {
return false;
}
}
void KDebugger::programBreak()
{
if (m_haveExecutable && m_programRunning) {
m_d->interruptInferior();
}
}
void KDebugger::programArgs(QWidget* parent)
{
if (m_haveExecutable) {
QStringList allOptions = m_d->boolOptionList();
PgmArgs dlg(parent, m_executable, m_envVars, allOptions);
dlg.setArgs(m_programArgs);
dlg.setWd(m_programWD);
dlg.setOptions(m_boolOptions);
if (dlg.exec()) {
updateProgEnvironment(dlg.args(), dlg.wd(),
dlg.envVars(), dlg.options());
}
}
}
void KDebugger::programSettings(QWidget* parent)
{
if (!m_haveExecutable)
return;
ProgramSettings dlg(parent, m_executable);
dlg.m_chooseDriver.setDebuggerCmd(m_debuggerCmd);
dlg.m_output.setTTYLevel(m_ttyLevel);
if (dlg.exec() == QDialog::Accepted)
{
m_debuggerCmd = dlg.m_chooseDriver.debuggerCmd();
m_ttyLevel = TTYLevel(dlg.m_output.ttyLevel());
}
}
bool KDebugger::setBreakpoint(QString file, int lineNo,
const DbgAddr& address, bool temporary)
{
if (!isReady()) {
return false;
}
BrkptIterator bp = breakpointByFilePos(file, lineNo, address);
if (bp == m_brkpts.end())
{
/*
* No such breakpoint, so set a new one. If we have an address, we
* set the breakpoint exactly there. Otherwise we use the file name
* plus line no.
*/
Breakpoint* bp = new Breakpoint;
bp->temporary = temporary;
if (address.isEmpty())
{
bp->fileName = file;
bp->lineNo = lineNo;
}
else
{
bp->address = address;
}
setBreakpoint(bp, false);
}
else
{
/*
* If the breakpoint is disabled, enable it; if it's enabled,
* delete that breakpoint.
*/
if (bp->enabled) {
deleteBreakpoint(bp);
} else {
enableDisableBreakpoint(bp);
}
}
return true;
}
void KDebugger::setBreakpoint(Breakpoint* bp, bool queueOnly)
{
CmdQueueItem* cmd = executeBreakpoint(bp, queueOnly);
cmd->m_brkpt = bp; // used in newBreakpoint()
}
CmdQueueItem* KDebugger::executeBreakpoint(const Breakpoint* bp, bool queueOnly)
{
CmdQueueItem* cmd;
if (!bp->text.isEmpty())
{
/*
* The breakpoint was set using the text box in the breakpoint
* list. This is the only way in which watchpoints are set.
*/
if (bp->type == Breakpoint::watchpoint) {
cmd = m_d->executeCmd(DCwatchpoint, bp->text);
} else {
cmd = m_d->executeCmd(DCbreaktext, bp->text);
}
}
else if (bp->address.isEmpty())
{
// strip off directory part of file name
QString file = QFileInfo(bp->fileName).fileName();
if (queueOnly) {
cmd = m_d->queueCmd(bp->temporary ? DCtbreakline : DCbreakline,
file, bp->lineNo, DebuggerDriver::QMoverride);
} else {
cmd = m_d->executeCmd(bp->temporary ? DCtbreakline : DCbreakline,
file, bp->lineNo);
}
}
else
{
if (queueOnly) {
cmd = m_d->queueCmd(bp->temporary ? DCtbreakaddr : DCbreakaddr,
bp->address.asString(), DebuggerDriver::QMoverride);
} else {
cmd = m_d->executeCmd(bp->temporary ? DCtbreakaddr : DCbreakaddr,
bp->address.asString());
}
}
return cmd;
}
bool KDebugger::infoLine(QString file, int lineNo, const DbgAddr& addr)
{
if (isReady() && !m_programRunning) {
CmdQueueItem* cmd = m_d->executeCmd(DCinfoline, file, lineNo);
cmd->m_addr = addr;
return true;
} else {
return false;
}
}
bool KDebugger::enableDisableBreakpoint(QString file, int lineNo,
const DbgAddr& address)
{
BrkptIterator bp = breakpointByFilePos(file, lineNo, address);
return enableDisableBreakpoint(bp);
}
bool KDebugger::enableDisableBreakpoint(BrkptIterator bp)
{
if (bp == m_brkpts.end())
return false;
/*
* Toggle enabled/disabled state.
*
* The driver is not bothered if we are modifying an orphaned
* breakpoint.
*/
if (!bp->isOrphaned()) {
if (!canChangeBreakpoints()) {
return false;
}
m_d->executeCmd(bp->enabled ? DCdisable : DCenable, bp->id);
} else {
bp->enabled = !bp->enabled;
emit breakpointsChanged();
}
return true;
}
bool KDebugger::conditionalBreakpoint(BrkptIterator bp,
const QString& condition,
int ignoreCount)
{
if (bp == m_brkpts.end())
return false;
/*
* Change the condition and ignore count.
*
* The driver is not bothered if we are removing an orphaned
* breakpoint.
*/
if (!bp->isOrphaned()) {
if (!canChangeBreakpoints()) {
return false;
}
bool changed = false;
if (bp->condition != condition) {
// change condition
m_d->executeCmd(DCcondition, condition, bp->id);
changed = true;
}
if (bp->ignoreCount != ignoreCount) {
// change ignore count
m_d->executeCmd(DCignore, bp->id, ignoreCount);
changed = true;
}
if (changed) {
// get the changes
m_d->queueCmd(DCinfobreak, DebuggerDriver::QMoverride);
}
} else {
bp->condition = condition;
bp->ignoreCount = ignoreCount;
emit breakpointsChanged();
}
return true;
}
bool KDebugger::deleteBreakpoint(BrkptIterator bp)
{
if (bp == m_brkpts.end())
return false;
/*
* Remove the breakpoint.
*
* The driver is not bothered if we are removing an orphaned
* breakpoint.
*/
if (!bp->isOrphaned()) {
if (!canChangeBreakpoints()) {
return false;
}
m_d->executeCmd(DCdelete, bp->id);
} else {
m_brkpts.erase(bp);
emit breakpointsChanged();
}
return false;
}
bool KDebugger::canSingleStep()
{
return isReady() && m_programActive && !m_programRunning;
}
bool KDebugger::canChangeBreakpoints()
{
return isReady() && !m_programRunning;
}
bool KDebugger::canStart()
{
return isReady() && !m_programActive;
}
bool KDebugger::isReady() const
{
return m_haveExecutable &&
m_d != 0 && m_d->canExecuteImmediately();
}
bool KDebugger::isIdle() const
{
return m_d == 0 || m_d->isIdle();
}
//////////////////////////////////////////////////////////
// debugger driver
bool KDebugger::startDriver()
{
emit debuggerStarting(); /* must set m_inferiorTerminal */
/*
* If the per-program command string is empty, use the global setting
* (which might also be empty, in which case the driver uses its
* default).
*/
m_explicitKill = false;
if (!m_d->startup(m_debuggerCmd)) {
return false;
}
/*
* If we have an output terminal, we use it. Otherwise we will run the
* program with input and output redirected to /dev/null. Other
* redirections are also necessary depending on the tty emulation
* level.
*/
int redirect = RDNstdin|RDNstdout|RDNstderr; /* redirect everything */
if (!m_inferiorTerminal.isEmpty()) {
switch (m_ttyLevel) {
default:
case ttyNone:
// redirect everything
break;
case ttySimpleOutputOnly:
redirect = RDNstdin;
break;
case ttyFull:
redirect = 0;
break;
}
}
m_d->executeCmd(DCtty, m_inferiorTerminal, redirect);
return true;
}
void KDebugger::stopDriver()
{
m_explicitKill = true;
if (m_attachedPid.isEmpty()) {
m_d->terminate();
} else {
m_d->detachAndTerminate();
}
/*
* We MUST wait until the slot gdbExited() has been called. But to
* avoid a deadlock, we wait only for some certain maximum time. Should
* this timeout be reached, the only reasonable thing one could do then
* is exiting kdbg.
*/
QApplication::processEvents(QEventLoop::AllEvents, 1000);
int maxTime = 20; /* about 20 seconds */
while (m_haveExecutable && maxTime > 0) {
// give gdb time to die (and send a SIGCLD)
::sleep(1);
--maxTime;
QApplication::processEvents(QEventLoop::AllEvents, 1000);
}
}
void KDebugger::gdbExited()
{
/*
* Save settings, but only if gdb has already processed "info line
* main", otherwise we would save an empty config file, because it
* isn't read in until then!
*/
if (m_programConfig != 0) {
if (m_haveExecutable) {
saveProgramSettings();
m_programConfig->sync();
}
delete m_programConfig;
m_programConfig = 0;
}
// erase types
delete m_typeTable;
m_typeTable = 0;
if (m_explicitKill) {
TRACE(m_d->driverName() + " exited normally");
} else {
QString msg = i18n("%1 exited unexpectedly.\n"
"Restart the session (e.g. with File|Executable).");
KMessageBox::error(parentWidget(), msg.arg(m_d->driverName()));
}
// reset state
m_haveExecutable = false;
m_executable = "";
m_programActive = false;
m_programRunning = false;
m_explicitKill = false;
m_debuggerCmd = QString(); /* use global setting at next start! */
m_attachedPid = QString(); /* we are no longer attached to a process */
m_ttyLevel = ttyFull;
m_brkpts.clear();
// erase PC
emit updatePC(QString(), -1, DbgAddr(), 0);
}
QString KDebugger::getConfigForExe(const QString& name)
{
QFileInfo fi(name);
QString dir = fi.absolutePath();
// The session file for the given executable consists of
// a hash of the directory, followed by the program name.
// Assume that the first 15 positions of the hash are unique;
// this keeps the file names short.
QString hash = KMD5(dir.toUtf8()).base64Digest();
hash.replace('/', QString()); // avoid directory separators
QString pgmConfigFile = hash.left(15) + "-" + fi.fileName();
pgmConfigFile = KStandardDirs::locateLocal("sessions", pgmConfigFile);
TRACE("program config file = " + pgmConfigFile);
return pgmConfigFile;
}
void KDebugger::openProgramConfig(const QString& name)
{
ASSERT(m_programConfig == 0);
QString pgmConfigFile = getConfigForExe(name);
m_programConfig = new KConfig(pgmConfigFile);
// this leaves a clue behind in the config file which
// executable it applies to; it is mostly intended for
// users peeking into the file
KConfigGroup g = m_programConfig->group(GeneralGroup);
g.writeEntry("ExecutableFile", name);
}
const char EnvironmentGroup[] = "Environment";
const char WatchGroup[] = "Watches";
const char FileVersion[] = "FileVersion";
const char ProgramArgs[] = "ProgramArgs";
const char WorkingDirectory[] = "WorkingDirectory";
const char OptionsSelected[] = "OptionsSelected";
const char Variable[] = "Var%d";
const char Value[] = "Value%d";
const char ExprFmt[] = "Expr%d";
void KDebugger::saveProgramSettings()
{
ASSERT(m_programConfig != 0);
KConfigGroup gg = m_programConfig->group(GeneralGroup);
gg.writeEntry(FileVersion, 1);
gg.writeEntry(ProgramArgs, m_programArgs);
gg.writeEntry(WorkingDirectory, m_programWD);
gg.writeEntry(OptionsSelected, m_boolOptions.toList());
gg.writeEntry(DebuggerCmdStr, m_debuggerCmd);
gg.writeEntry(TTYLevelEntry, int(m_ttyLevel));
QString driverName;
if (m_d != 0)
driverName = m_d->driverName();
gg.writeEntry(DriverNameEntry, driverName);
// write environment variables
m_programConfig->deleteGroup(EnvironmentGroup);
KConfigGroup eg = m_programConfig->group(EnvironmentGroup);
QString varName;
QString varValue;
int i = 0;
for (std::map::iterator it = m_envVars.begin(); it != m_envVars.end(); ++it, ++i)
{
varName.sprintf(Variable, i);
varValue.sprintf(Value, i);
eg.writeEntry(varName, it->first);
eg.writeEntry(varValue, it->second);
}
saveBreakpoints(m_programConfig);
// watch expressions
// first get rid of whatever was in this group
m_programConfig->deleteGroup(WatchGroup);
// then start a new group
KConfigGroup wg = m_programConfig->group(WatchGroup);
int watchNum = 0;
foreach (QString expr, m_watchVariables.exprList()) {
varName.sprintf(ExprFmt, watchNum++);
wg.writeEntry(varName, expr);
}
// give others a chance
emit saveProgramSpecific(m_programConfig);
}
void KDebugger::overrideProgramArguments(const QString& args)
{
ASSERT(m_programConfig != 0);
KConfigGroup g = m_programConfig->group(GeneralGroup);
g.writeEntry(ProgramArgs, args);
}
void KDebugger::restoreProgramSettings()
{
ASSERT(m_programConfig != 0);
KConfigGroup gg = m_programConfig->group(GeneralGroup);
/*
* We ignore file version for now we will use it in the future to
* distinguish different versions of this configuration file.
*/
// m_debuggerCmd has been read in already
// m_ttyLevel has been read in already
QString pgmArgs = gg.readEntry(ProgramArgs);
QString pgmWd = gg.readEntry(WorkingDirectory);
QSet boolOptions = QSet::fromList(gg.readEntry(OptionsSelected, QStringList()));
m_boolOptions.clear();
// read environment variables
KConfigGroup eg = m_programConfig->group(EnvironmentGroup);
m_envVars.clear();
std::map pgmVars;
QString varName;
QString varValue;
for (int i = 0;; ++i) {
varName.sprintf(Variable, i);
varValue.sprintf(Value, i);
if (!eg.hasKey(varName)) {
/* entry not present, assume that we've hit them all */
break;
}
QString name = eg.readEntry(varName, QString());
if (name.isEmpty()) {
// skip empty names
continue;
}
EnvVar* var = &pgmVars[name];
var->value = eg.readEntry(varValue, QString());
var->status = EnvVar::EVnew;
}
updateProgEnvironment(pgmArgs, pgmWd, pgmVars, boolOptions);
restoreBreakpoints(m_programConfig);
// watch expressions
KConfigGroup wg = m_programConfig->group(WatchGroup);
m_watchVariables.clear();
for (int i = 0;; ++i) {
varName.sprintf(ExprFmt, i);
if (!wg.hasKey(varName)) {
/* entry not present, assume that we've hit them all */
break;
}
QString expr = wg.readEntry(varName, QString());
if (expr.isEmpty()) {
// skip empty expressions
continue;
}
addWatch(expr);
}
// give others a chance
emit restoreProgramSpecific(m_programConfig);
}
/**
* Reads the debugger command line from the program settings. The config
* group must have been set by the caller.
*/
QString KDebugger::readDebuggerCmd(const KConfigGroup& g)
{
QString debuggerCmd = g.readEntry(DebuggerCmdStr);
// always let the user confirm the debugger cmd if we are root
if (::geteuid() == 0)
{
if (!debuggerCmd.isEmpty()) {
QString msg = i18n(
"The settings for this program specify "
"the following debugger command:\n%1\n"
"Shall this command be used?");
if (KMessageBox::warningYesNo(parentWidget(), msg.arg(debuggerCmd))
!= KMessageBox::Yes)
{
// don't use it
debuggerCmd = QString();
}
}
}
return debuggerCmd;
}
/*
* Breakpoints are saved one per group.
*/
const char BPGroup[] = "Breakpoint %d";
const char File[] = "File";
const char Line[] = "Line";
const char Text[] = "Text";
const char Address[] = "Address";
const char Temporary[] = "Temporary";
const char Enabled[] = "Enabled";
const char Condition[] = "Condition";
void KDebugger::saveBreakpoints(KConfig* config)
{
QString groupName;
int i = 0;
for (BrkptIterator bp = m_brkpts.begin(); bp != m_brkpts.end(); ++bp)
{
if (bp->type == Breakpoint::watchpoint)
continue; /* don't save watchpoints */
groupName.sprintf(BPGroup, i++);
/* remove remmants */
config->deleteGroup(groupName);
KConfigGroup g = config->group(groupName);
if (!bp->text.isEmpty()) {
/*
* The breakpoint was set using the text box in the breakpoint
* list. We do not save the location by filename+line number,
* but instead honor what the user typed (a function name, for
* example, which could move between sessions).
*/
g.writeEntry(Text, bp->text);
} else if (!bp->fileName.isEmpty()) {
g.writeEntry(File, bp->fileName);
g.writeEntry(Line, bp->lineNo);
/*
* Addresses are hardly correct across sessions, so we don't
* save it.
*/
} else {
g.writeEntry(Address, bp->address.asString());
}
g.writeEntry(Temporary, bp->temporary);
g.writeEntry(Enabled, bp->enabled);
if (!bp->condition.isEmpty())
g.writeEntry(Condition, bp->condition);
// we do not save the ignore count
}
// delete remaining groups
// we recognize that a group is present if there is an Enabled entry
for (;; i++) {
groupName.sprintf(BPGroup, i);
if (!config->group(groupName).hasKey(Enabled)) {
/* group not present, assume that we've hit them all */
break;
}
config->deleteGroup(groupName);
}
}
void KDebugger::restoreBreakpoints(KConfig* config)
{
QString groupName;
/*
* We recognize the end of the list if there is no Enabled entry
* present.
*/
for (int i = 0;; i++) {
groupName.sprintf(BPGroup, i);
KConfigGroup g = config->group(groupName);
if (!g.hasKey(Enabled)) {
/* group not present, assume that we've hit them all */
break;
}
Breakpoint* bp = new Breakpoint;
bp->fileName = g.readEntry(File);
bp->lineNo = g.readEntry(Line, -1);
bp->text = g.readEntry(Text);
bp->address = g.readEntry(Address);
// check consistency
if ((bp->fileName.isEmpty() || bp->lineNo < 0) &&
bp->text.isEmpty() &&
bp->address.isEmpty())
{
delete bp;
continue;
}
bp->enabled = g.readEntry(Enabled, true);
bp->temporary = g.readEntry(Temporary, false);
bp->condition = g.readEntry(Condition);
/*
* Add the breakpoint.
*/
setBreakpoint(bp, false);
// the new breakpoint is disabled or conditionalized later
// in newBreakpoint()
}
m_d->queueCmd(DCinfobreak, DebuggerDriver::QMoverride);
}
// parse output of command cmd
void KDebugger::parse(CmdQueueItem* cmd, const char* output)
{
ASSERT(cmd != 0); /* queue mustn't be empty */
TRACE(QString(__PRETTY_FUNCTION__) + " parsing " + output);
switch (cmd->m_cmd) {
case DCtargetremote:
// the output (if any) is uninteresting
case DCsetargs:
case DCtty:
// there is no output
case DCsetenv:
case DCunsetenv:
case DCsetoption:
/* if value is empty, we see output, but we don't care */
break;
case DCcd:
/* display gdb's message in the status bar */
m_d->parseChangeWD(output, m_statusMessage);
emit updateStatusMessage();
break;
case DCinitialize:
break;
case DCexecutable:
if (m_d->parseChangeExecutable(output, m_statusMessage))
{
// success; restore breakpoints etc.
if (m_programConfig != 0) {
restoreProgramSettings();
}
// load file containing main() or core file
if (!m_corefile.isEmpty())
{
// load core file
loadCoreFile();
}
else if (!m_attachedPid.isEmpty())
{
m_d->queueCmd(DCattach, m_attachedPid, DebuggerDriver::QMoverride);
m_programActive = true;
m_programRunning = true;
}
else if (!m_remoteDevice.isEmpty())
{
// handled elsewhere
}
else
{
m_d->queueCmd(DCinfolinemain, DebuggerDriver::QMnormal);
}
if (!m_statusMessage.isEmpty())
emit updateStatusMessage();
} else {
QString msg = m_d->driverName() + ": " + m_statusMessage;
KMessageBox::sorry(parentWidget(), msg);
m_executable = "";
m_corefile = ""; /* don't process core file */
m_haveExecutable = false;
}
break;
case DCcorefile:
// in any event we have an executable at this point
m_haveExecutable = true;
if (m_d->parseCoreFile(output)) {
// loading a core is like stopping at a breakpoint
m_programActive = true;
handleRunCommands(output);
// do not reset m_corefile
} else {
// report error
QString msg = m_d->driverName() + ": " + QString(output);
KMessageBox::sorry(parentWidget(), msg);
// if core file was loaded from command line, revert to info line main
if (!cmd->m_byUser) {
m_d->queueCmd(DCinfolinemain, DebuggerDriver::QMnormal);
}
m_corefile = QString(); /* core file not available any more */
}
break;
case DCinfolinemain:
// ignore the output, marked file info follows
m_haveExecutable = true;
break;
case DCinfolocals:
// parse local variables
if (output[0] != '\0') {
handleLocals(output);
}
break;
case DCinforegisters:
handleRegisters(output);
break;
case DCexamine:
handleMemoryDump(output);
break;
case DCinfoline:
handleInfoLine(cmd, output);
break;
case DCdisassemble:
handleDisassemble(cmd, output);
break;
case DCframe:
handleFrameChange(output);
updateAllExprs();
break;
case DCbt:
handleBacktrace(output);
updateAllExprs();
break;
case DCprint:
handlePrint(cmd, output);
break;
case DCprintDeref:
handlePrintDeref(cmd, output);
break;
case DCattach:
m_haveExecutable = true;
// fall through
case DCrun:
case DCcont:
case DCstep:
case DCstepi:
case DCnext:
case DCnexti:
case DCfinish:
case DCuntil:
case DCthread:
handleRunCommands(output);
break;
case DCkill:
m_programRunning = m_programActive = false;
// erase PC
emit updatePC(QString(), -1, DbgAddr(), 0);
break;
case DCbreaktext:
case DCbreakline:
case DCtbreakline:
case DCbreakaddr:
case DCtbreakaddr:
case DCwatchpoint:
newBreakpoint(cmd, output);
// fall through
case DCdelete:
case DCenable:
case DCdisable:
// these commands need immediate response
m_d->queueCmd(DCinfobreak, DebuggerDriver::QMoverrideMoreEqual);
break;
case DCinfobreak:
// note: this handler must not enqueue a command, since
// DCinfobreak is used at various different places.
updateBreakList(output);
break;
case DCfindType:
handleFindType(cmd, output);
break;
case DCprintStruct:
case DCprintQStringStruct:
case DCprintWChar:
handlePrintStruct(cmd, output);
break;
case DCinfosharedlib:
handleSharedLibs(output);
break;
case DCcondition:
case DCignore:
// we are not interested in the output
break;
case DCinfothreads:
handleThreadList(output);
break;
case DCsetpc:
handleSetPC(output);
break;
case DCsetvariable:
handleSetVariable(cmd, output);
break;
}
}
void KDebugger::backgroundUpdate()
{
/*
* If there are still expressions that need to be updated, then do so.
*/
if (m_programActive)
evalExpressions();
}
void KDebugger::handleRunCommands(const char* output)
{
uint flags = m_d->parseProgramStopped(output, m_statusMessage);
emit updateStatusMessage();
m_programActive = flags & DebuggerDriver::SFprogramActive;
// refresh files if necessary
if (flags & DebuggerDriver::SFrefreshSource) {
TRACE("re-reading files");
emit executableUpdated();
}
/*
* Try to set any orphaned breakpoints now.
*/
for (BrkptIterator bp = m_brkpts.begin(); bp != m_brkpts.end(); ++bp)
{
if (bp->isOrphaned()) {
TRACE(QString("re-trying brkpt loc: %2 file: %3 line: %1")
.arg(bp->lineNo).arg(bp->location, bp->fileName));
CmdQueueItem* cmd = executeBreakpoint(&*bp, true);
cmd->m_existingBrkpt = bp->id; // used in newBreakpoint()
flags |= DebuggerDriver::SFrefreshBreak;
}
}
/*
* If we stopped at a breakpoint, we must update the breakpoint list
* because the hit count changes. Also, if the breakpoint was temporary
* it would go away now.
*/
if ((flags & (DebuggerDriver::SFrefreshBreak|DebuggerDriver::SFrefreshSource)) ||
stopMayChangeBreakList())
{
m_d->queueCmd(DCinfobreak, DebuggerDriver::QMoverride);
}
/*
* If we haven't listed the shared libraries yet, do so. We must do
* this before we emit any commands that list variables, since the type
* libraries depend on the shared libraries.
*/
if (!m_sharedLibsListed) {
// must be a high-priority command!
m_d->executeCmd(DCinfosharedlib);
}
// get the backtrace if the program is running
if (m_programActive) {
m_d->queueCmd(DCbt, DebuggerDriver::QMoverride);
} else {
// program finished: erase PC
emit updatePC(QString(), -1, DbgAddr(), 0);
// dequeue any commands in the queues
m_d->flushCommands();
}
/* Update threads list */
if (m_programActive && (flags & DebuggerDriver::SFrefreshThreads)) {
m_d->queueCmd(DCinfothreads, DebuggerDriver::QMoverride);
}
m_programRunning = false;
emit programStopped();
}
void KDebugger::slotInferiorRunning()
{
m_programRunning = true;
}
void KDebugger::updateAllExprs()
{
if (!m_programActive)
return;
// retrieve local variables
m_d->queueCmd(DCinfolocals, DebuggerDriver::QMoverride);
// retrieve registers
m_d->queueCmd(DCinforegisters, DebuggerDriver::QMoverride);
// get new memory dump
if (!m_memoryExpression.isEmpty()) {
queueMemoryDump(false);
}
// update watch expressions
foreach (QString expr, m_watchVariables.exprList()) {
m_watchEvalExpr.push_back(expr);
}
}
void KDebugger::updateProgEnvironment(const QString& args, const QString& wd,
const std::map& newVars,
const QSet& newOptions)
{
m_programArgs = args;
m_d->executeCmd(DCsetargs, m_programArgs);
TRACE("new pgm args: " + m_programArgs + "\n");
m_programWD = wd.trimmed();
if (!m_programWD.isEmpty()) {
m_d->executeCmd(DCcd, m_programWD);
TRACE("new wd: " + m_programWD + "\n");
}
// update environment variables
for (std::map::const_iterator i = newVars.begin(); i != newVars.end(); ++i)
{
QString var = i->first;
const EnvVar* val = &i->second;
switch (val->status) {
case EnvVar::EVnew:
case EnvVar::EVdirty:
m_envVars[var] = val->value;
// update value
m_d->executeCmd(DCsetenv, var, val->value);
break;
case EnvVar::EVdeleted:
// delete value
m_d->executeCmd(DCunsetenv, var);
m_envVars.erase(var);
break;
default:
ASSERT(false);
case EnvVar::EVclean:
// variable not changed
break;
}
}
// update options
foreach (QString opt, newOptions - m_boolOptions)
{
// option is not set, set it
m_d->executeCmd(DCsetoption, opt, 1);
}
foreach (QString opt, m_boolOptions - newOptions)
{
// option is set, unset it
m_d->executeCmd(DCsetoption, opt, 0);
}
m_boolOptions = newOptions;
}
void KDebugger::handleLocals(const char* output)
{
// retrieve old list of local variables
QStringList oldVars = m_localVariables.exprList();
/*
* Get local variables.
*/
std::list newVars;
parseLocals(output, newVars);
/*
* Clear any old VarTree item pointers, so that later we don't access
* dangling pointers.
*/
m_localVariables.clearPendingUpdates();
/*
* Match old variables against new ones.
*/
for (QStringList::ConstIterator n = oldVars.begin(); n != oldVars.end(); ++n) {
// lookup this variable in the list of new variables
std::list::iterator v = newVars.begin();
while (v != newVars.end() && (*v)->m_name != *n)
++v;
if (v == newVars.end()) {
// old variable not in the new variables
TRACE("old var deleted: " + *n);
VarTree* v = m_localVariables.topLevelExprByName(*n);
if (v != 0) {
m_localVariables.removeExpr(v);
}
} else {
// variable in both old and new lists: update
TRACE("update var: " + *n);
m_localVariables.updateExpr(*v, *m_typeTable);
// remove the new variable from the list
delete *v;
newVars.erase(v);
}
}
// insert all remaining new variables
while (!newVars.empty())
{
ExprValue* v = newVars.front();
TRACE("new var: " + v->m_name);
m_localVariables.insertExpr(v, *m_typeTable);
delete v;
newVars.pop_front();
}
}
void KDebugger::parseLocals(const char* output, std::list& newVars)
{
std::list vars;
m_d->parseLocals(output, vars);
QString origName; /* used in renaming variables */
while (!vars.empty())
{
ExprValue* variable = vars.front();
vars.pop_front();
/*
* When gdb prints local variables, those from the innermost block
* come first. We run through the list of already parsed variables
* to find duplicates (ie. variables that hide local variables from
* a surrounding block). We keep the name of the inner variable, but
* rename those from the outer block so that, when the value is
* updated in the window, the value of the variable that is
* _visible_ changes the color!
*/
int block = 0;
origName = variable->m_name;
for (std::list::iterator v = newVars.begin(); v != newVars.end(); ++v) {
if (variable->m_name == (*v)->m_name) {
// we found a duplicate, change name
block++;
QString newName = origName + " (" + QString().setNum(block) + ")";
variable->m_name = newName;
}
}
newVars.push_back(variable);
}
}
bool KDebugger::handlePrint(CmdQueueItem* cmd, const char* output)
{
ASSERT(cmd->m_expr != 0);
ExprValue* variable = m_d->parsePrintExpr(output, true);
if (variable == 0)
return false;
// set expression "name"
variable->m_name = cmd->m_expr->getText();
{
TRACE("update expr: " + cmd->m_expr->getText());
cmd->m_exprWnd->updateExpr(cmd->m_expr, variable, *m_typeTable);
delete variable;
}
evalExpressions(); /* enqueue dereferenced pointers */
return true;
}
bool KDebugger::handlePrintDeref(CmdQueueItem* cmd, const char* output)
{
ASSERT(cmd->m_expr != 0);
ExprValue* variable = m_d->parsePrintExpr(output, true);
if (variable == 0)
return false;
// set expression "name"
variable->m_name = cmd->m_expr->getText();
{
/*
* We must insert a dummy parent, because otherwise variable's value
* would overwrite cmd->m_expr's value.
*/
ExprValue* dummyParent = new ExprValue(variable->m_name, VarTree::NKplain);
dummyParent->m_varKind = VarTree::VKdummy;
// the name of the parsed variable is the address of the pointer
QString addr = "*" + cmd->m_expr->value();
variable->m_name = addr;
variable->m_nameKind = VarTree::NKaddress;
dummyParent->m_child = variable;
// expand the first level for convenience
variable->m_initiallyExpanded = true;
TRACE("update ptr: " + cmd->m_expr->getText());
cmd->m_exprWnd->updateExpr(cmd->m_expr, dummyParent, *m_typeTable);
delete dummyParent;
}
evalExpressions(); /* enqueue dereferenced pointers */
return true;
}
// parse the output of bt
void KDebugger::handleBacktrace(const char* output)
{
m_btWindow.clear();
std::list stack;
m_d->parseBackTrace(output, stack);
if (!stack.empty()) {
std::list::iterator frm = stack.begin();
// first frame must set PC
// note: frm->lineNo is zero-based
emit updatePC(frm->fileName, frm->lineNo, frm->address, frm->frameNo);
for (; frm != stack.end(); ++frm) {
QString func;
if (frm->var != 0)
func = frm->var->m_name;
else
func = frm->fileName + ":" + QString().setNum(frm->lineNo+1);
m_btWindow.addItem(func);
TRACE("frame " + func + " (" + frm->fileName + ":" +
QString().setNum(frm->lineNo+1) + ")");
}
}
}
void KDebugger::gotoFrame(int frame)
{
m_d->executeCmd(DCframe, frame);
}
void KDebugger::handleFrameChange(const char* output)
{
QString fileName;
int frameNo;
int lineNo;
DbgAddr address;
if (m_d->parseFrameChange(output, frameNo, fileName, lineNo, address)) {
/* lineNo can be negative here if we can't find a file name */
emit updatePC(fileName, lineNo, address, frameNo);
} else {
emit updatePC(fileName, -1, address, frameNo);
}
}
void KDebugger::evalExpressions()
{
// evaluate expressions in the following order:
// watch expressions
// pointers in local variables
// pointers in watch expressions
// types in local variables
// types in watch expressions
// struct members in local variables
// struct members in watch expressions
VarTree* exprItem = 0;
if (!m_watchEvalExpr.empty())
{
QString expr = m_watchEvalExpr.front();
m_watchEvalExpr.pop_front();
exprItem = m_watchVariables.topLevelExprByName(expr);
}
if (exprItem != 0) {
CmdQueueItem* cmd = m_d->queueCmd(DCprint, exprItem->getText(), DebuggerDriver::QMoverride);
// remember which expr this was
cmd->m_expr = exprItem;
cmd->m_exprWnd = &m_watchVariables;
} else {
ExprWnd* wnd;
#define POINTER(widget) \
wnd = &widget; \
exprItem = widget.nextUpdatePtr(); \
if (exprItem != 0) goto pointer
#define STRUCT(widget) \
wnd = &widget; \
exprItem = widget.nextUpdateStruct(); \
if (exprItem != 0) goto ustruct
#define TYPE(widget) \
wnd = &widget; \
exprItem = widget.nextUpdateType(); \
if (exprItem != 0) goto type
repeat:
POINTER(m_localVariables);
POINTER(m_watchVariables);
STRUCT(m_localVariables);
STRUCT(m_watchVariables);
TYPE(m_localVariables);
TYPE(m_watchVariables);
#undef POINTER
#undef STRUCT
#undef TYPE
return;
pointer:
// we have an expression to send
dereferencePointer(wnd, exprItem, false);
return;
ustruct:
// paranoia
if (exprItem->m_type == 0 || exprItem->m_type == TypeInfo::unknownType())
goto repeat;
evalInitialStructExpression(exprItem, wnd, false);
return;
type:
/*
* Sometimes a VarTree gets registered twice for a type update. So
* it may happen that it has already been updated. Hence, we ignore
* it here and go on to the next task.
*/
if (exprItem->m_type != 0)
goto repeat;
determineType(wnd, exprItem);
}
}
void KDebugger::dereferencePointer(ExprWnd* wnd, VarTree* exprItem,
bool immediate)
{
ASSERT(exprItem->m_varKind == VarTree::VKpointer);
QString expr = exprItem->computeExpr();
TRACE("dereferencing pointer: " + expr);
CmdQueueItem* cmd;
if (immediate) {
cmd = m_d->queueCmd(DCprintDeref, expr, DebuggerDriver::QMoverrideMoreEqual);
} else {
cmd = m_d->queueCmd(DCprintDeref, expr, DebuggerDriver::QMoverride);
}
// remember which expr this was
cmd->m_expr = exprItem;
cmd->m_exprWnd = wnd;
}
void KDebugger::determineType(ExprWnd* wnd, VarTree* exprItem)
{
ASSERT(exprItem->m_varKind == VarTree::VKstruct);
QString expr = exprItem->computeExpr();
TRACE("get type of: " + expr);
CmdQueueItem* cmd;
cmd = m_d->queueCmd(DCfindType, expr, DebuggerDriver::QMoverride);
// remember which expr this was
cmd->m_expr = exprItem;
cmd->m_exprWnd = wnd;
}
void KDebugger::handleFindType(CmdQueueItem* cmd, const char* output)
{
QString type;
if (m_d->parseFindType(output, type))
{
ASSERT(cmd != 0 && cmd->m_expr != 0);
const TypeInfo* info = m_typeTable->lookup(type);
if (info == 0) {
/*
* We've asked gdb for the type of the expression in
* cmd->m_expr, but it returned a name we don't know. The base
* class (and member) types have been checked already (at the
* time when we parsed that particular expression). Now it's
* time to derive the type from the base classes as a last
* resort.
*/
info = cmd->m_expr->inferTypeFromBaseClass();
// if we found a type through this method, register an alias
if (info != 0) {
TRACE("infered alias: " + type);
m_typeTable->registerAlias(type, info);
}
}
if (info == 0) {
TRACE("unknown type "+type);
cmd->m_expr->m_type = TypeInfo::unknownType();
} else {
cmd->m_expr->m_type = info;
/* since this node has a new type, we get its value immediately */
evalInitialStructExpression(cmd->m_expr, cmd->m_exprWnd, false);
return;
}
}
evalExpressions(); /* queue more of them */
}
void KDebugger::handlePrintStruct(CmdQueueItem* cmd, const char* output)
{
VarTree* var = cmd->m_expr;
ASSERT(var != 0);
ASSERT(var->m_varKind == VarTree::VKstruct);
ExprValue* partExpr;
if (cmd->m_cmd == DCprintQStringStruct) {
partExpr = m_d->parseQCharArray(output, false, m_typeTable->qCharIsShort());
} else if (cmd->m_cmd == DCprintWChar) {
partExpr = m_d->parseQCharArray(output, false, true);
} else {
partExpr = m_d->parsePrintExpr(output, false);
}
bool errorValue =
partExpr == 0 ||
/* we only allow simple values at the moment */
partExpr->m_child != 0;
QString partValue;
if (errorValue)
{
partValue = "?""?""?"; // 2 question marks in a row would be a trigraph
} else {
partValue = partExpr->m_value;
}
delete partExpr;
partExpr = 0;
/*
* Updating a struct value works like this: var->m_partialValue holds
* the value that we have gathered so far (it's been initialized with
* var->m_type->m_displayString[0] earlier). Each time we arrive here,
* we append the printed result followed by the next
* var->m_type->m_displayString to var->m_partialValue.
*
* If the expression we just evaluated was a guard expression, and it
* resulted in an error, we must not evaluate the real expression, but
* go on to the next index. (We must still add the question marks to
* the value).
*
* Next, if this was the length expression, we still have not seen the
* real expression, but the length of a QString.
*/
ASSERT(var->m_exprIndex >= 0 && var->m_exprIndex <= typeInfoMaxExpr);
if (errorValue || !var->m_exprIndexUseGuard)
{
// add current partValue (which might be the question marks)
var->m_partialValue += partValue;
var->m_exprIndex++; /* next part */
var->m_exprIndexUseGuard = true;
var->m_partialValue += var->m_type->m_displayString[var->m_exprIndex];
}
else
{
// this was a guard expression that succeeded
// go for the real expression
var->m_exprIndexUseGuard = false;
}
/* go for more sub-expressions if needed */
if (var->m_exprIndex < var->m_type->m_numExprs) {
/* queue a new print command with quite high priority */
evalStructExpression(var, cmd->m_exprWnd, true);
return;
}
cmd->m_exprWnd->updateStructValue(var);
evalExpressions(); /* enqueue dereferenced pointers */
}
/* queues the first printStruct command for a struct */
void KDebugger::evalInitialStructExpression(VarTree* var, ExprWnd* wnd, bool immediate)
{
var->m_exprIndex = 0;
if (var->m_type != TypeInfo::wchartType())
{
var->m_exprIndexUseGuard = true;
var->m_partialValue = var->m_type->m_displayString[0];
evalStructExpression(var, wnd, immediate);
}
else
{
var->m_exprIndexUseGuard = false;
QString expr = var->computeExpr();
CmdQueueItem* cmd = m_d->queueCmd(DCprintWChar, expr,
immediate ? DebuggerDriver::QMoverrideMoreEqual
: DebuggerDriver::QMoverride);
// remember which expression this was
cmd->m_expr = var;
cmd->m_exprWnd = wnd;
}
}
/** queues a printStruct command; var must have been initialized correctly */
void KDebugger::evalStructExpression(VarTree* var, ExprWnd* wnd, bool immediate)
{
QString base = var->computeExpr();
QString expr;
if (var->m_exprIndexUseGuard) {
expr = var->m_type->m_guardStrings[var->m_exprIndex];
if (expr.isEmpty()) {
// no guard, omit it and go to expression
var->m_exprIndexUseGuard = false;
}
}
if (!var->m_exprIndexUseGuard) {
expr = var->m_type->m_exprStrings[var->m_exprIndex];
}
expr.replace("%s", base);
DbgCommand dbgCmd = DCprintStruct;
// check if this is a QString::Data
if (expr.left(15) == "/QString::Data ")
{
if (m_typeTable->parseQt2QStrings())
{
expr = expr.mid(15, expr.length()); /* strip off /QString::Data */
dbgCmd = DCprintQStringStruct;
} else {
/*
* This should not happen: the type libraries should be set up
* in a way that this can't happen. If this happens
* nevertheless it means that, eg., kdecore was loaded but qt2
* was not (only qt2 enables the QString feature).
*/
// TODO: remove this "print"; queue the next printStruct instead
expr = "*0";
}
}
TRACE("evalStruct: " + expr + (var->m_exprIndexUseGuard ? " // guard" : " // real"));
CmdQueueItem* cmd = m_d->queueCmd(dbgCmd, expr,
immediate ? DebuggerDriver::QMoverrideMoreEqual
: DebuggerDriver::QMnormal);
// remember which expression this was
cmd->m_expr = var;
cmd->m_exprWnd = wnd;
}
void KDebugger::handleSharedLibs(const char* output)
{
// parse the table of shared libraries
m_sharedLibs = m_d->parseSharedLibs(output);
m_sharedLibsListed = true;
// get type libraries
m_typeTable->loadLibTypes(m_sharedLibs);
// hand over the QString data cmd
m_d->setPrintQStringDataCmd(m_typeTable->printQStringDataCmd());
}
CmdQueueItem* KDebugger::loadCoreFile()
{
return m_d->queueCmd(DCcorefile, m_corefile, DebuggerDriver::QMoverride);
}
void KDebugger::slotExpanding(QTreeWidgetItem* item)
{
VarTree* exprItem = static_cast(item);
if (exprItem->m_varKind != VarTree::VKpointer) {
return;
}
ExprWnd* wnd = static_cast(item->treeWidget());
dereferencePointer(wnd, exprItem, true);
}
// add the expression in the edit field to the watch expressions
void KDebugger::addWatch(const QString& t)
{
QString expr = t.trimmed();
// don't add a watched expression again
if (expr.isEmpty() || m_watchVariables.topLevelExprByName(expr) != 0)
return;
ExprValue e(expr, VarTree::NKplain);
m_watchVariables.insertExpr(&e, *m_typeTable);
// if we are boring ourselves, send down the command
if (m_programActive) {
m_watchEvalExpr.push_back(expr);
if (m_d->isIdle()) {
evalExpressions();
}
}
}
// delete a toplevel watch expression
void KDebugger::slotDeleteWatch()
{
// delete only allowed while debugger is idle; or else we might delete
// the very expression the debugger is currently working on...
if (m_d == 0 || !m_d->isIdle())
return;
VarTree* item = m_watchVariables.selectedItem();
if (item == 0 || !item->isToplevelExpr())
return;
// remove the variable from the list to evaluate
std::list::iterator i =
std::find(m_watchEvalExpr.begin(), m_watchEvalExpr.end(), item->getText());
if (i != m_watchEvalExpr.end()) {
m_watchEvalExpr.erase(i);
}
m_watchVariables.removeExpr(item);
// item is invalid at this point!
}
void KDebugger::handleRegisters(const char* output)
{
emit registersChanged(m_d->parseRegisters(output));
}
/*
* The output of the DCbreak* commands has more accurate information about
* the file and the line number.
*
* All newly set breakpoints are inserted in the m_brkpts, even those that
* were not set sucessfully. The unsuccessful breakpoints ("orphaned
* breakpoints") are assigned negative ids, and they are tried to set later
* when the program stops again at a breakpoint.
*/
void KDebugger::newBreakpoint(CmdQueueItem* cmd, const char* output)
{
BrkptIterator bp;
if (cmd->m_brkpt != 0) {
// a new breakpoint, put it in the list
assert(cmd->m_brkpt->id == 0);
m_brkpts.push_back(*cmd->m_brkpt);
delete cmd->m_brkpt;
bp = m_brkpts.end();
--bp;
} else {
// an existing breakpoint was retried
assert(cmd->m_existingBrkpt != 0);
bp = breakpointById(cmd->m_existingBrkpt);
if (bp == m_brkpts.end())
return;
}
// parse the output to determine success or failure
int id;
QString file;
int lineNo;
QString address;
if (!m_d->parseBreakpoint(output, id, file, lineNo, address))
{
/*
* Failure, the breakpoint could not be set. If this is a new
* breakpoint, assign it a negative id. We look for the minimal id
* of all breakpoints (that are already in the list) to get the new
* id.
*/
if (bp->id == 0)
{
int minId = 0;
for (BrkptIterator i = m_brkpts.begin(); i != m_brkpts.end(); ++i) {
if (i->id < minId)
minId = i->id;
}
bp->id = minId-1;
}
return;
}
// The breakpoint was successfully set.
if (bp->id <= 0)
{
// this is a new or orphaned breakpoint:
// set the remaining properties
if (!bp->enabled) {
m_d->executeCmd(DCdisable, id);
}
if (!bp->condition.isEmpty()) {
m_d->executeCmd(DCcondition, bp->condition, id);
}
}
bp->id = id;
bp->fileName = file;
bp->lineNo = lineNo;
if (!address.isEmpty())
bp->address = address;
}
void KDebugger::updateBreakList(const char* output)
{
// get the new list
std::list brks;
m_d->parseBreakList(output, brks);
// merge existing information into the new list
// then swap the old and new lists
for (BrkptIterator bp = brks.begin(); bp != brks.end(); ++bp)
{
BrkptIterator i = breakpointById(bp->id);
if (i != m_brkpts.end())
{
// preserve accurate location information
// note that xsldbg doesn't have a location in
// the listed breakpoint if it has just been set
// therefore, we copy it as well if necessary
bp->text = i->text;
if (!i->fileName.isEmpty()) {
bp->fileName = i->fileName;
bp->lineNo = i->lineNo;
}
}
}
// orphaned breakpoints must be copied
for (BrkptIterator bp = m_brkpts.begin(); bp != m_brkpts.end(); ++bp)
{
if (bp->isOrphaned())
brks.push_back(*bp);
}
m_brkpts.swap(brks);
emit breakpointsChanged();
}
// look if there is at least one temporary breakpoint
// or a watchpoint
bool KDebugger::stopMayChangeBreakList() const
{
for (BrkptROIterator bp = m_brkpts.begin(); bp != m_brkpts.end(); ++bp)
{
if (bp->temporary || bp->type == Breakpoint::watchpoint)
return true;
}
return false;
}
KDebugger::BrkptIterator KDebugger::breakpointByFilePos(QString file, int lineNo,
const DbgAddr& address)
{
// look for exact file name match
for (BrkptIterator bp = m_brkpts.begin(); bp != m_brkpts.end(); ++bp)
{
if (bp->lineNo == lineNo &&
bp->fileName == file &&
(address.isEmpty() || bp->address == address))
{
return bp;
}
}
// not found, so try basename
file = QFileInfo(file).fileName();
for (BrkptIterator bp = m_brkpts.begin(); bp != m_brkpts.end(); ++bp)
{
// get base name of breakpoint's file
QString basename = QFileInfo(bp->fileName).fileName();
if (bp->lineNo == lineNo &&
basename == file &&
(address.isEmpty() || bp->address == address))
{
return bp;
}
}
// not found
return m_brkpts.end();
}
KDebugger::BrkptIterator KDebugger::breakpointById(int id)
{
for (BrkptIterator bp = m_brkpts.begin(); bp != m_brkpts.end(); ++bp)
{
if (bp->id == id) {
return bp;
}
}
// not found
return m_brkpts.end();
}
void KDebugger::slotValuePopup(const QString& expr)
{
// search the local variables for a match
VarTree* v = m_localVariables.topLevelExprByName(expr);
if (v == 0) {
// not found, check watch expressions
v = m_watchVariables.topLevelExprByName(expr);
if (v == 0) {
// try a member of 'this'
v = m_localVariables.topLevelExprByName("this");
if (v != 0)
v = ExprWnd::ptrMemberByName(v, expr);
if (v == 0) {
// nothing found; do nothing
return;
}
}
}
// construct the tip
QString tip = v->getText() + " = ";
if (!v->value().isEmpty())
{
tip += v->value();
}
else
{
// no value: we use some hint
switch (v->m_varKind) {
case VarTree::VKstruct:
tip += "{...}";
break;
case VarTree::VKarray:
tip += "[...]";
break;
default:
tip += "?""?""?"; // 2 question marks in a row would be a trigraph
break;
}
}
emit valuePopup(tip);
}
void KDebugger::slotDisassemble(const QString& fileName, int lineNo)
{
if (m_haveExecutable) {
CmdQueueItem* cmd = m_d->queueCmd(DCinfoline, fileName, lineNo,
DebuggerDriver::QMoverrideMoreEqual);
cmd->m_fileName = fileName;
cmd->m_lineNo = lineNo;
}
}
void KDebugger::handleInfoLine(CmdQueueItem* cmd, const char* output)
{
QString addrFrom, addrTo;
if (cmd->m_lineNo >= 0) {
// disassemble
if (m_d->parseInfoLine(output, addrFrom, addrTo)) {
// got the address range, now get the real code
CmdQueueItem* c = m_d->queueCmd(DCdisassemble, addrFrom, addrTo,
DebuggerDriver::QMoverrideMoreEqual);
c->m_fileName = cmd->m_fileName;
c->m_lineNo = cmd->m_lineNo;
} else {
// no code
emit disassembled(cmd->m_fileName, cmd->m_lineNo, std::list());
}
} else {
// set program counter
if (m_d->parseInfoLine(output, addrFrom, addrTo)) {
// move the program counter to the start address
m_d->executeCmd(DCsetpc, addrFrom);
}
}
}
void KDebugger::handleDisassemble(CmdQueueItem* cmd, const char* output)
{
emit disassembled(cmd->m_fileName, cmd->m_lineNo,
m_d->parseDisassemble(output));
}
void KDebugger::handleThreadList(const char* output)
{
emit threadsChanged(m_d->parseThreadList(output));
}
void KDebugger::setThread(int id)
{
m_d->queueCmd(DCthread, id, DebuggerDriver::QMoverrideMoreEqual);
}
void KDebugger::setMemoryExpression(const QString& memexpr)
{
m_memoryExpression = memexpr;
// queue the new expression
if (!m_memoryExpression.isEmpty() &&
isProgramActive() &&
!isProgramRunning())
{
queueMemoryDump(true);
}
}
void KDebugger::queueMemoryDump(bool immediate)
{
m_d->queueCmd(DCexamine, m_memoryExpression, m_memoryFormat,
immediate ? DebuggerDriver::QMoverrideMoreEqual :
DebuggerDriver::QMoverride);
}
void KDebugger::handleMemoryDump(const char* output)
{
std::list memdump;
QString msg = m_d->parseMemoryDump(output, memdump);
emit memoryDumpChanged(msg, memdump);
}
void KDebugger::setProgramCounter(const QString& file, int line, const DbgAddr& addr)
{
if (addr.isEmpty()) {
// find address of the specified line
CmdQueueItem* cmd = m_d->executeCmd(DCinfoline, file, line);
cmd->m_lineNo = -1; /* indicates "Set PC" UI command */
} else {
// move the program counter to that address
m_d->executeCmd(DCsetpc, addr.asString());
}
}
void KDebugger::handleSetPC(const char* /*output*/)
{
// TODO: handle errors
// now go to the top-most frame
// this also modifies the program counter indicator in the UI
gotoFrame(0);
}
void KDebugger::slotValueEdited(VarTree* expr, const QString& text)
{
if (text.simplified().isEmpty())
return; /* no text entered: ignore request */
ExprWnd* wnd = static_cast(expr->treeWidget());
TRACE(QString().sprintf("Changing %s to ",
wnd->name()) + text);
// determine the lvalue to edit
QString lvalue = expr->computeExpr();
CmdQueueItem* cmd = m_d->executeCmd(DCsetvariable, lvalue, text);
cmd->m_expr = expr;
cmd->m_exprWnd = wnd;
}
void KDebugger::handleSetVariable(CmdQueueItem* cmd, const char* output)
{
QString msg = m_d->parseSetVariable(output);
if (!msg.isEmpty())
{
// there was an error; display it in the status bar
m_statusMessage = msg;
emit updateStatusMessage();
return;
}
// get the new value
QString expr = cmd->m_expr->computeExpr();
CmdQueueItem* printCmd =
m_d->queueCmd(DCprint, expr, DebuggerDriver::QMoverrideMoreEqual);
printCmd->m_expr = cmd->m_expr;
printCmd->m_exprWnd = cmd->m_exprWnd;
}
#include "debugger.moc"
kdbg-2.5.4/kdbg/debugger.h 0000664 0000000 0000000 00000043403 12237506721 0015306 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#ifndef DEBUGGER_H
#define DEBUGGER_H
#include
#include
#include
#include
The local variables window is opened using View|Locals. The local
variables window displays the contents of the local variables at the currently
selected stack frame.
The set of local variables that are displayed is determined by the stack
frame that is selected in the stack window.
Variable values that changed between stops of the program are displayed
in red color.
The values of most variables can be changed. For this purpose, press F2
while the input focus is in the window or choose Edit value from the
context menu. Then edit the value and hit Enter. Note that you cannot modify
the strings that char* values point to in this way, just the pointer
value.
Using the context menu you can move
the active variable or structure member to the watched
expressions window.
The memory dump window is displayed using View|Memory. It displays
the contents of the program's memory at arbitrary adresses.
To display memory contents, enter an address in the edit field. The
address need not be given in hexadecimal form - it can be an expression.
You can specifiy a format how the memory contents shall be displayed
by chooseing the appropriate options from the popup menu that you invoke
by clicking the right mouse button.
A number of address expressions are remembered. You can recall such
an expression from the drop-down list. Please note that the address expression
is remembered together with the format.
If you don't need to investigate memory contents, it is recommended
that you clear the expression so that no memory dump is displayed - this
speeds up the debugging process.
The program output window is displayed using View|Output. The output
window captures text that is written to stdout and stderr by the program
being debugged.
The output window does not allow to type input for the program and it
features only minimal terminal emulation: \n (line-feed),
\t (horizontal tab), and \r (carriage-return)
are treated. These capabilities are usually sufficient to debug GUI programs
which only write debugging output to stdout and stderr.
When a program is debugged with KDbg for the first time, the program
output window is not used. The reason for this is that KDbg cannot
know whether the program requires sophisticated terminal emulation or if
it expects input through a terminal. So, a terminal emulator program is
used by default. In order to redirect the output to the output window,
you must do the following:
Open the Settings dialog by selecting Settings|This Program.
Switch to the Output tab.
Choose Only output, simple terminal emulation and click OK.
Reload the program by selecting it from the list in File|Recent Executables.
You can clear the contents of the output window by selecting Clear
from the popup menu that appears when you click the right mouse button.
If the last line of the output is visible, the window always scrolls
automatically so that the last line remains visible when new output arrives.
If, however, you manually scroll up so that the last line is not visible,
the visible portion of text will not change.
In this dialog, program specific settings can be selected. It is invoked
by Settings|This Program. The settings apply only to the currently loaded
executable and will be saved across sessions.
Important note: The chosen settings will only
apply the next time the executable is loaded into KDbg. This means that
after pressing
OK in this dialog, you must reload the executable
using File|Recent Executables!!
In this section, the debugger to be used for the program can be chosen.
How to invoke GDB
Enter the command to invoke gdb. Leave this field empty to
use the default gdb command as specified in the global
options. When you are cross-compiling and remote-debugging, you will
probably want to use a different gdb suitable for the target platform.
The default command is gdb --fullname --nx. Be sure to specify
at least --fullname if you change the gdb command.
If you remove this command switch, KDbg will not work.
Output
In this section, the terminal emulation under which the program will run
can be selected.
No input and output
Check this option if your program does not receive input from
the terminal and you do not want to see the output that the program writes
to stdout and stderr (if any). All three standard channels
(stdin, stdout, and stderr) are effectively
redirected to /dev/null.
Only output, simple terminal emulation
Check this option if your program does not receive input from
the terminal (stdin will be redirected to /dev/null),
and the output that it writes to stdout and stderr does
not require sophisticated terminal emulation. The output will be shown
in the Output window.
Full terminal emulation
Check this option if your program reads input from stdin
or if the output to stdout or stderr requires terminal
emulation. A terminal emulator will be invoked as specified in the global
options.
Whenever the program stops, KDbg displays the contents of the CPU registers
in the register dump window. To display this window, choose View|Registers.
The registers are grouped by the kind of register. The window contains 3 columns:
The column Register displays the register name.
The column Value displays the contents of the registers in a more
or less raw form. The raw form is usually displayed as hexadecimal numbers,
even the contents of floating point registers.
The column Decoded value displays the contents of the registers
in a decoded form. For arithmetic registers this is generally a signed
decimal value, floating point registers are displayed as floating point
numbers, the flag registers are sometimes decoded into named flags.
By clicking the right mouse button a context menu is popped up which lets
you select how the value in the third column is displayed.
You can change the type to use for all registers of a group at once if you
choose the format for the group header.
The source code window is the main window and is always visible.
The source code window displays program source code. At the left of
each source line is an "active area". It displays a pointer to indicate
which source line the program currently executes, and it indicates on which
source lines breakpoints have been set.
New breakpoints can be set by clicking into the active area with the
left mouse button. An existing breakpoint can be enabled and disabled by
clicking with the middle mouse button.
The tiny plus '+' between the "active area" and the source line can
be clicked on. If you do so, the source line's assembler code will be displayed.
The plus turns into a minus '-', which, if clicked, will hide the disassembled
code.
Mostly, source code windows are opened automatically. To open a new
source file manually, click the right mouse button and choose Open Source
or choose File|Open Source.
The stack window is displayed using View|Stack. The stack window
lists the stack frames, i.e. the functions that the program has entered,
but not yet left.
The innermost frame (where the program currently executes) is shown
at the top.
To switch to a different stack frame, simply click on that stack frame.
The source window displays the source line
where the function invocation took place and the local
variables window and the watch window change
to reflect the local variables of the selected stack frame.
The threads window is displayed using View|Threads. The threads
window lists the active threads of the program.
Note: Debugging threaded programs must be supported by
the version of gdb that is used - it is not a feature of KDbg. For Linux
systems this works best with gdb5 and later. However, at the time of this writing gdb
still poorly supports threads on NPTL- (New Posix Threads Library) enabled
systems (glibc 2.3.x and kernel 2.6.x).
The contents of the threads window are updated every time the program
is stopped by the debugger. (That is, the window does not reflect
the state while the program is running.)
The first column shows the thread ID, the second column identifies the
location where the thread currently executes.
The marker in front of the line tells which thread currently is active:
The stack window displays the active threads's
backtrace.
The local variables window displays the active
thread's local variables.
The watch window uses the active thread's local
variables to evaluate the expressions.
By clicking a listed thread, the active thread is switched, and the corresponding
windows are updated. In particular, the source
window displays the location where the active thread is currently halted.
You can use breakpoints as bookmarks: Just set a breakpoint and disable
it. Later, you can quickly come back to that breakpoint by double-clicking
it in the breakpoint list (or select it and click View Code). Since
breakpoints are persistent (i.e. KDbg remembers them across invocations
of a program), you get them back next time you invoke KDbg for that particular
program.
You can display a value in the watch section in different ways by prepending
gdb's format specifiers in front of the variable to display. E.g. /x
var.member displays the var.member in hexadecimal notation.
You can set breakpoints in a source files that belong to a shared library.
Such breakpoints will be marked as orphaned if the program is not active.
Orphaned breakpoints are not effective.
In order to make them effective, the program must stop at a time when the shared
library is loaded. For this it is usually sufficient to set a breakpoint in
main(). At the time when this breakpoint is hit, the orphaned breakpoints
in the shared library become effective.
Debugging multi-threaded programs on NPTL-enabled Linux systems (kernel 2.6.x
or later and glibc 2.3.x or later) may sometimes fails; gdb stops the program
at unexpected instances. In this case the following may help (using bash):
LD_ASSUME_KERNEL=2.4.19 kdbg myprogram
I.e. you run KDbg from the command line such that the old
Linuxthreads implementation is used.
KDbg can display a short description of structured types, so
that it is not necessary to expand the variable in the local
variables window or watched expressions window.
The information which member variable is displayed is stored in type
tables. There is generally one type table per shared library.
KDbg's default type tables are located under $prefix/share/apps/kdbg/types.
User defined type tables can be placed in ${KDEHOME}/share/apps/kdbg/types, where
${KDEHOME} is ~/.kde if it is not a defined environment variable.
The file names end with .kdbgtt. Example: The type table for libqt.so
is named qt.kdbgtt.
User defined type tables override the type tables provided by the system.
A type table file obeys the regular KDE configuration file syntax. The
file has the following groups:
A group [Type Table] which lists the types and information how
the debugger can identify whether the program is linked against the library.
A group for each type which has information about how the value of such
a type is displayed by KDbg.
In order to determine which type tables apply to the program being debugged
KDbg lists the shared libraries it is linked to. Then it matches the names
against the ShlibRE entries of all type tables. Those that match
are used. If a type appears in several type tables, it is unspecified which
one will be used.
KDbg's type recognition only works for libraries that are linked dynamically
to the program being debugged.
The [Type Table] group
This group contains the following entries:
Types1, Types2, etc. These entries name the types,
separated by commas.
Each of the entries can list any number of types. The entries must be numbered
consecutively (KDbg stops reading at the first gap), although an entry may be
empty (i.e. contain no type at all).
Sometimes the order in which the names are listed is important
(see Alias types below).
ShlibRE. KDbg uses this entry to determine if the type table applies
to the program being debugged. For this purpose KDbg determines the shared
libraries to which the program is linked. If any of the libraries matches
this entry, the type table applies. The entry is a Qt regular
expression.
Note that back-slashes must be doubled because the back-slash is an escape
character in the configuration file syntax.
LibDisplayName. This entry is used in lists where the available
type tables are listed to identify this type table.
This is not used currently.
EnableBuiltin lists extensions that must be enabled if this
library is used. Currently, two builtins are supported:
QString::Data is used to display unicode strings of Qt's QString
class. See below.
QCharIsShort is used only in connection with QString::Data
to specify that a unicode character is stored in an object of type short.
See qt3.kdbgtt for examples.
In the case of regular types the names of types should follow the output of the
whatis gdb command less any const, spaces, or trailing
&.
If the type contains a a comma in its name, it must be escaped with a backslash.
But note that the comma should not be escaped in the type's group (which is described
in the next section).
In the case of template types the name can be arbitrary because the type's group
will mention the template name and a type parameter list.
The type's group
There is one group for each type that is named exactly as the type.
Each group contains the following entries:
An optional Template entry that specifies the exact template type
name as it is reported by gdb's whatis command. However, it is
possible to replace template parameter types at the top-most level by an
asterisk *, which acts as a wildcard: It matches one
template type argument that is reported by whatis (except that an
asterisk in the last position matches all remaining template type arguments).
Display determines how the value of the type is displayed by KDbg.
The string must contain 1 to 5 percent characters '%'. These are
replaced by the results of the expressions printed by the Exprx
entries.
One or more of Expr1, Expr2, etc. Each of them must contain
one or more %s sequence, which will be replaced by the expression
whose value is investigated. The so constructed expression is submitted
to gdb, and the result substituted back for the corresponding percent character
in the Display string.
An optional FunctionGuardx that is associated with the corresponding Exprx.
If the evaluation of the resulting gdb expression returns an error, the corresponding expression from Exprx is not evaluated. (This is used to guard function calls.)
Alias names an alias type. If this entry is present, the type
is treated like the specified type. That alias type must appear before
this type in the Typesx entries in the Type Table.
Currently the number of expressions per type is limited to
5. This can easily be changed if it's too restrictive, but I recommend
not to go to that limit at all - it will slow down the debugging process.
KDbg recognizes a special extension that is used to display Qt 2.x's and Qt 3.x's
unicode strings: If an Exprx is prepended with /QString::Data,
it is assumed that the result of the expression is a pointer to a QString::Data.
The value displayed is the unicode string that this instance of QString::Data
represents (which can be QString::null if it is Qt's well-defined
null string or (null) if the unicode member is the null
pointer). See qt2.kdbgtt for examples.
Tip: It is not necessary to define derived types if they ought to be
treated the same as the base class - KDbg can deduce derived types and
uses the type specification of the (leftmost) base class. You can use the
Alias
entry to quickly specify that a type should be treated like a non-leftmost
base class for a multiple-inheritance class.
An example
The example shows how QString and QRect are defined
in qt3.kdbgtt. Furthermore, the template type QValueVector
is defined. This example applies to Qt 3.x, which is located in shared library
whose name ends in libqt-mt.so.3.
The name of the template type, QValueVector is irrelevant.
The exact type name is specified under the Template= entry.
It specifies a single wildcard so that it applies to all specializations.
In order to evaluate the expression that was supplied in the %s
only once, the result is stored in a temporary gdb variable and reused later in
the same expression.
Note that it is safer to wrap the %s in parentheses.
The watched expressions window is opened using View|Watched Expressions.
It displays arbitrary expressions.
To add an expression, type it into the edit field and press Enter or
click Add. To remove an expression, click on it (choose the root
of the expression) and click Del.
You can also move a variable or structure member from the local
variables window to this window using the context menu in the local
variables window.
The values of most expressions can be changed. For this purpose, press F2
while the input focus is in the window. Then edit the value and hit Enter.
Note that you cannot modify the strings that char* values point
to in this way, just the pointer value.
Watched expressions are stored across debugging sessions. It is recommended
that you remove expressions that your don't need any longer because that
speeds up the debugging process.
KDbg allows to debug XSLT (XML stylesheet translation) scripts using
xsldbg, which must be available
on your system.
Specifying the script and an XML file to transform
XSLT mode is automatically entered if a "program" is loaded
that has a file name that ends in .xsl. In addition, the
command line option-l XSL can be
specified to explicitly choose this mode.
To debug an XSLT script it is necessary to specify an XML data file
that the script can transform. This is done in the
Program Arguments dialog, where the XML
file is specified in the Program Arguments edit box.
擸蚝纖屺豁阽 趿畛秸恌 衃畛恮殮衭 芶秺 蚥衶奷虭 珆攽騰. 蟴嗩玵
疻冾芶恅桲娸 桫賻晟 趿畛秸恌, 冾婘砣虭 趿 欶 恓 俵杻祰籥籣, .. 嗩
俵趿蚕 蚎玾珃婥 珆攽騰.
kdbg-2.5.4/kdbg/envvar.h 0000664 0000000 0000000 00000001061 12237506721 0015015 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#ifndef ENVVAR_H
#define ENVVAR_H
/*
* Description of environment variables. Note that the name of the variable
* is given as the key in the map, so we don't repeat it here.
*/
class QTreeWidgetItem;
struct EnvVar {
QString value;
enum EnvVarStatus { EVclean, EVdirty, EVnew, EVdeleted };
EnvVarStatus status;
QTreeWidgetItem* item;
};
#endif // ENVVAR_H
kdbg-2.5.4/kdbg/exprwnd.cpp 0000664 0000000 0000000 00000051321 12237506721 0015542 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#include "exprwnd.h"
#include "exprwnd.moc"
#include "typetable.h"
#include
#include
#include
#include
#include
#include
#include
#include /* icons */
#include /* i18n */
#include "mydebug.h"
VarTree::VarTree(VarTree* parent, ExprValue* v) :
QTreeWidgetItem(parent),
m_varKind(v->m_varKind),
m_nameKind(v->m_nameKind),
m_type(0),
m_exprIndex(0),
m_exprIndexUseGuard(false),
m_baseValue(v->m_value),
m_baseChanged(false),
m_structChanged(false)
{
setText(v->m_name);
updateValueText();
if (v->m_child != 0 || m_varKind == VarTree::VKpointer)
setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
else
setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator);
setExpanded(v->m_initiallyExpanded);
}
VarTree::VarTree(ExprWnd* parent, ExprValue* v) :
QTreeWidgetItem(parent),
m_varKind(VKsimple),
m_nameKind(VarTree::NKplain),
m_type(0),
m_exprIndex(0),
m_exprIndexUseGuard(false),
m_baseValue(v->m_value),
m_baseChanged(false),
m_structChanged(false)
{
setText(v->m_name);
updateValueText();
}
VarTree::~VarTree()
{
}
QString VarTree::computeExpr() const
{
// top-level items are special
if (isToplevelExpr())
return getText();
// get parent expr
VarTree* par = static_cast(parent());
QString parentExpr = par->computeExpr();
// skip this item's name if it is a base class or anonymous struct or union
if (m_nameKind == NKtype || m_nameKind == NKanonymous) {
return parentExpr;
}
/* augment by this item's text */
QString result;
/* if this is an address, dereference it */
if (m_nameKind == NKaddress) {
ASSERT(par->m_varKind == VKpointer);
result = "*" + parentExpr;
return result;
}
switch (par->m_varKind) {
case VKarray:
{
QString index = getText();
int i = 1;
// skip past the index
while (index[i].isDigit())
i++;
/*
* Some array indices are actually ranges due to repeated array
* values. We use the first index in these cases.
*/
if (index[i] != ']') {
// remove second index
index.remove(i, index.length()-i-1);
}
result = "(" + parentExpr + ")" + index;
}
break;
case VKstruct:
result = "(" + parentExpr + ")." + getText();
break;
case VKsimple: /* parent can't be simple */
case VKpointer: /* handled in NKaddress */
case VKdummy: /* can't occur at all */
ASSERT(false);
result = parentExpr; /* paranoia */
break;
}
return result;
}
bool VarTree::isToplevelExpr() const
{
return parent() == 0;
}
bool VarTree::isAncestorEq(const VarTree* child) const
{
const QTreeWidgetItem* c = child;
while (c != 0 && c != this) {
c = c->parent();
}
return c != 0;
}
bool VarTree::updateValue(const QString& newValue)
{
// check whether the value changed
bool prevValueChanged = m_baseChanged;
if ((m_baseChanged = m_baseValue != newValue)) {
m_baseValue = newValue;
updateValueText();
setForeground(1, QBrush(QColor(Qt::red)));
} else if (prevValueChanged) {
setForeground(1, treeWidget()->palette().text());
}
/*
* We must repaint the cell if the value changed. If it did not change,
* we still must repaint the cell if the value changed previously,
* because the color of the display must be changed (from red to
* black).
*/
return m_baseChanged || prevValueChanged;
}
bool VarTree::updateStructValue(const QString& newValue)
{
// check whether the value changed
bool prevValueChanged = m_structChanged;
if ((m_structChanged = m_structValue != newValue)) {
m_structValue = newValue;
updateValueText();
setForeground(1, QBrush(QColor(Qt::red)));
} else if (prevValueChanged) {
setForeground(1, treeWidget()->palette().text());
}
/*
* We must repaint the cell if the value changed. If it did not change,
* we still must repaint the cell if the value changed previously,
* because the color of the display must be changed (from red to
* black).
*/
return m_structChanged || prevValueChanged;
}
void VarTree::updateValueText()
{
if (m_baseValue.isEmpty()) {
setText(1, m_structValue);
} else if (m_structValue.isEmpty()) {
setText(1, m_baseValue);
} else {
setText(1, m_baseValue + " " + m_structValue);
}
}
void VarTree::inferTypesOfChildren(ProgramTypeTable& typeTable)
{
/*
* Type inference works like this: We use type information of those
* children that have a type name in their name (base classes) or in
* their value (pointers)
*/
// first recurse children
for (int i = 0; i < childCount(); i++)
{
child(i)->inferTypesOfChildren(typeTable);
}
// if this is a pointer, get the type from the value (less the pointer)
if (m_varKind == VKpointer) {
if (isWcharT())
{
/*
* wchart_t pointers must be treated as struct, because the array
* of characters is printed similar to how QStrings are decoded.
*/
m_varKind = VKstruct;
setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator);
}
// don't know how to do this cleanly
} else if (m_varKind == VKstruct) {
// check if this is a base class part
if (m_nameKind == NKtype) {
const QString& typeName =
getText().mid(1, getText().length()-2); // strip < and >
m_type = typeTable.lookup(typeName);
/* if we don't have a type yet, get it from the base class */
if (m_type == 0) {
m_type = inferTypeFromBaseClass();
/*
* If there is a known type now, it is the one from the
* first base class whose type we know.
*/
}
/*
* If we still don't have a type, the type is really unknown.
*/
if (m_type == 0) {
m_type = TypeInfo::unknownType();
}
} // else
/*
* This is not a base class part. We don't assign a type so
* that later we can ask gdb.
*/
}
}
// the value contains the pointer type in parenthesis
bool VarTree::isWcharT() const
{
return value().startsWith("(const wchar_t *)") ||
value().startsWith("(wchar_t *)");
}
/*
* Get the type of the first base class whose type we know.
*/
const TypeInfo* VarTree::inferTypeFromBaseClass()
{
if (m_varKind == VKstruct) {
for (int i = 0; i < childCount(); i++)
{
VarTree* child = VarTree::child(i);
// only check base class parts (i.e. type names)
if (child->m_nameKind != NKtype)
break;
if (child->m_type != 0 &&
child->m_type != TypeInfo::unknownType())
{
// got a type!
return child->m_type;
}
}
}
return 0;
}
ExprValue::ExprValue(const QString& name, VarTree::NameKind aKind) :
m_name(name),
m_varKind(VarTree::VKsimple),
m_nameKind(aKind),
m_child(0),
m_next(0),
m_initiallyExpanded(false)
{
}
ExprValue::~ExprValue()
{
delete m_child;
delete m_next;
}
void ExprValue::appendChild(ExprValue* newChild)
{
if (m_child == 0) {
m_child = newChild;
} else {
// walk chain of children to find the last one
ExprValue* last = m_child;
while (last->m_next != 0)
last = last->m_next;
last->m_next = newChild;
}
newChild->m_next = 0; // just to be sure
}
int ExprValue::childCount() const
{
int i = 0;
ExprValue* c = m_child;
while (c) {
++i;
c = c->m_next;
}
return i;
}
ExprWnd::ExprWnd(QWidget* parent, const QString& colHeader) :
QTreeWidget(parent),
m_edit(0)
{
QTreeWidgetItem* pHeaderItem = new QTreeWidgetItem();
pHeaderItem->setText(0, colHeader);
pHeaderItem->setText(1, i18n("Value"));
setHeaderItem(pHeaderItem);
header()->setResizeMode(0, QHeaderView::Interactive);
header()->setResizeMode(1, QHeaderView::Interactive);
setSortingEnabled(false); // do not sort items
setRootIsDecorated(true);
setAllColumnsShowFocus(true);
m_pixPointer = UserIcon("pointer.xpm");
if (m_pixPointer.isNull())
TRACE("Can't load pointer.xpm");
}
ExprWnd::~ExprWnd()
{
}
QStringList ExprWnd::exprList() const
{
QStringList exprs;
for (int i = 0; i < topLevelItemCount(); i++)
{
exprs.append(topLevelItem(i)->getText());
}
return exprs;
}
VarTree* ExprWnd::insertExpr(ExprValue* expr, ProgramTypeTable& typeTable)
{
// append a new dummy expression
VarTree* display = new VarTree(this, expr);
// replace it right away
updateExpr(display, expr, typeTable);
return display;
}
void ExprWnd::updateExpr(ExprValue* expr, ProgramTypeTable& typeTable)
{
// search the root variable
VarTree* item = 0;
for (int i = 0; i < topLevelItemCount(); i++)
{
if (topLevelItem(i)->getText() == expr->m_name) {
item = topLevelItem(i);
break;
}
}
if (item == 0) {
return;
}
// now update it
updateExprRec(item, expr, typeTable);
collectUnknownTypes(item);
}
void ExprWnd::updateExpr(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
{
updateExprRec(display, newValues, typeTable);
collectUnknownTypes(display);
}
/*
* returns true if there's a visible change
*/
void ExprWnd::updateExprRec(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
{
bool isExpanded = display->isExpanded();
/*
* If we are updating a pointer without children by a dummy, we don't
* collapse it, but simply insert the new children. This happens when a
* pointer has just been expanded by the user.
*/
if (display->m_varKind == VarTree::VKpointer &&
display->childCount() == 0 &&
newValues->m_varKind == VarTree::VKdummy)
{
replaceChildren(display, newValues);
return;
}
/*
* If the display and newValues have different kind or if their number
* of children is different, replace the whole sub-tree.
*/
if (// the next two lines mean: not(m_varKind remains unchanged)
!(newValues->m_varKind == VarTree::VKdummy ||
display->m_varKind == newValues->m_varKind)
||
(display->childCount() != newValues->childCount() &&
/*
* If this is a pointer and newValues doesn't have children, we
* don't replace the sub-tree; instead, below we mark this
* sub-tree for requiring an update.
*/
(display->m_varKind != VarTree::VKpointer ||
newValues->m_child != 0)))
{
if (isExpanded) {
display->setExpanded(false);
}
// since children changed, it is likely that the type has also changed
display->m_type = 0; /* will re-evaluate the type */
// display the new value
updateSingleExpr(display, newValues);
replaceChildren(display, newValues);
// update the m_varKind
if (newValues->m_varKind != VarTree::VKdummy) {
display->m_varKind = newValues->m_varKind;
if (newValues->m_child != 0 || newValues->m_varKind == VarTree::VKpointer)
display->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
else
display->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator);
}
// get some types (after the new m_varKind has been set!)
display->inferTypesOfChildren(typeTable);
// (note that the new value might not have a sub-tree at all)
return;
}
// display the new value
updateSingleExpr(display, newValues);
/*
* If this is an expanded pointer, record it for being updated.
*/
if (display->m_varKind == VarTree::VKpointer) {
if (isExpanded &&
// if newValues is a dummy, we have already updated this pointer
newValues->m_varKind != VarTree::VKdummy)
{
m_updatePtrs.push_back(display);
}
/*
* If the visible sub-tree has children, but newValues doesn't, we
* can stop here.
*/
if (newValues->m_child == 0) {
return;
}
}
ASSERT(display->childCount() == newValues->childCount());
// go for children
ExprValue* vNew = newValues->m_child;
for (int i = 0; i < display->childCount(); i++)
{
VarTree* vDisplay = display->child(i);
// check whether the names are the same
if (vDisplay->getText() != vNew->m_name) {
// set new name
vDisplay->setText(vNew->m_name);
}
// recurse
updateExprRec(vDisplay, vNew, typeTable);
vNew = vNew->m_next;
}
}
void ExprWnd::updateSingleExpr(VarTree* display, ExprValue* newValue)
{
/*
* If newValues is a VKdummy, we are only interested in its children.
* No need to update anything here.
*/
if (newValue->m_varKind == VarTree::VKdummy) {
return;
}
/*
* If this node is a struct and we know its type then we know how to
* find a nested value. So register the node for an update.
*
* wchar_t types are also treated specially here: We consider them
* as struct (has been set in inferTypesOfChildren()).
*/
if (display->m_varKind == VarTree::VKstruct &&
display->m_type != 0 &&
display->m_type != TypeInfo::unknownType())
{
ASSERT(newValue->m_varKind == VarTree::VKstruct);
if (display->m_type == TypeInfo::wchartType())
{
display->m_partialValue = "L";
}
else
display->m_partialValue = display->m_type->m_displayString[0];
m_updateStruct.push_back(display);
}
display->updateValue(newValue->m_value);
}
void ExprWnd::updateStructValue(VarTree* display)
{
ASSERT(display->m_varKind == VarTree::VKstruct);
display->updateStructValue(display->m_partialValue);
// reset the value
display->m_partialValue = "";
display->m_exprIndex = -1;
}
void ExprWnd::replaceChildren(VarTree* display, ExprValue* newValues)
{
ASSERT(display->childCount() == 0 || display->m_varKind != VarTree::VKsimple);
// delete all children of display
while (VarTree* c = display->child(0)) {
unhookSubtree(c);
delete c;
}
// insert copies of the newValues
for (ExprValue* v = newValues->m_child; v != 0; v = v->m_next)
{
VarTree* vNew = new VarTree(display, v);
// recurse
replaceChildren(vNew, v);
}
}
void ExprWnd::collectUnknownTypes(VarTree* var)
{
QTreeWidgetItemIterator i(var);
for (; *i; ++i)
{
checkUnknownType(static_cast(*i));
}
}
void ExprWnd::checkUnknownType(VarTree* var)
{
ASSERT(var->m_varKind != VarTree::VKpointer || var->m_nameKind != VarTree::NKtype);
if (var->m_type == 0 &&
var->m_varKind == VarTree::VKstruct &&
var->m_nameKind != VarTree::NKtype &&
var->m_nameKind != VarTree::NKanonymous)
{
if (!var->isWcharT())
{
/* this struct node doesn't have a type yet: register it */
m_updateType.push_back(var);
}
else
{
var->m_type = TypeInfo::wchartType();
var->m_partialValue = "L";
m_updateStruct.push_back(var);
}
}
// add pointer pixmap to pointers
if (var->m_varKind == VarTree::VKpointer) {
var->setPixmap(m_pixPointer);
}
}
QString ExprWnd::formatWCharPointer(QString value)
{
int pos = value.indexOf(") ");
if (pos > 0)
value = value.mid(pos+2);
return value + " L";
}
VarTree* ExprWnd::topLevelExprByName(const QString& name) const
{
for (int i = 0; i < topLevelItemCount(); i++)
{
if (topLevelItem(i)->getText() == name)
return topLevelItem(i);
}
return 0;
}
VarTree* ExprWnd::ptrMemberByName(VarTree* v, const QString& name)
{
// v must be a pointer variable, must have children
if (v->m_varKind != VarTree::VKpointer || v->childCount() == 0)
return 0;
// the only child of v is the pointer value that represents the struct
VarTree* item = v->child(0);
return memberByName(item, name);
}
VarTree* ExprWnd::memberByName(VarTree* v, const QString& name)
{
// search immediate children for name
for (int i = 0; i < v->childCount(); i++)
{
if (v->child(i)->getText() == name)
return v->child(i);
}
// try in base classes and members that are anonymous structs or unions
for (int i = 0; i < v->childCount(); i++)
{
VarTree* item = v->child(i);
if (item->m_nameKind == VarTree::NKtype ||
item->m_nameKind == VarTree::NKanonymous)
{
item = memberByName(item, name);
if (item != 0)
return item;
}
}
return 0;
}
void ExprWnd::removeExpr(VarTree* item)
{
unhookSubtree(item);
delete item;
}
void ExprWnd::unhookSubtree(VarTree* subTree)
{
// must remove any pointers scheduled for update from the list
unhookSubtree(m_updatePtrs, subTree);
unhookSubtree(m_updateType, subTree);
unhookSubtree(m_updateStruct, subTree);
emit removingItem(subTree);
}
void ExprWnd::unhookSubtree(std::list& list, VarTree* subTree)
{
if (subTree == 0)
return;
std::list::iterator i = list.begin();
while (i != list.end()) {
std::list::iterator checkItem = i;
++i;
if (subTree->isAncestorEq(*checkItem)) {
// checkItem is an item from subTree
list.erase(checkItem);
}
}
}
void ExprWnd::clearPendingUpdates()
{
m_updatePtrs.clear();
m_updateType.clear();
m_updateStruct.clear();
}
VarTree* ExprWnd::nextUpdatePtr()
{
VarTree* ptr = 0;
if (!m_updatePtrs.empty()) {
ptr = m_updatePtrs.front();
m_updatePtrs.pop_front();
}
return ptr;
}
VarTree* ExprWnd::nextUpdateType()
{
VarTree* ptr = 0;
if (!m_updateType.empty()) {
ptr = m_updateType.front();
m_updateType.pop_front();
}
return ptr;
}
VarTree* ExprWnd::nextUpdateStruct()
{
VarTree* ptr = 0;
if (!m_updateStruct.empty()) {
ptr = m_updateStruct.front();
m_updateStruct.pop_front();
}
return ptr;
}
void ExprWnd::editValue(VarTree* item, const QString& text)
{
if (m_edit == 0)
m_edit = new ValueEdit(this);
QRect r = visualItemRect(item);
int x = columnViewportPosition(1);
int y = r.y();
int w = columnWidth(1);
int h = r.height();
/*
* Make the edit widget at least 5 characters wide (but not wider than
* this widget). If less than half of this widget is used to display
* the text, scroll this widget so that half of it shows the text (or
* less than half of it if the text is shorter).
*/
QFontMetrics metr = m_edit->font();
int wMin = metr.width("88888");
if (w < wMin)
w = wMin;
int wThis = viewport()->width();
if (x >= wThis/2 && // less than half the width displays text
x+w > wThis) // not all text is visible
{
// scroll so that more text is visible
QScrollBar* pScrollBar = horizontalScrollBar();
int wScroll = qMin(x-wThis/2, x+w-wThis);
pScrollBar->setValue(pScrollBar->value() + wScroll);
x -= wScroll;
}
else if (x < 0)
{
// don't let the edit move out at the left
x = 0;
}
// make the edit box as wide as the visible column
QRect rect(x,y, wThis-x,h);
m_edit->setText(text);
m_edit->selectAll();
m_edit->setGeometry(rect);
m_edit->m_finished = false;
m_edit->m_item = item;
m_edit->show();
m_edit->setFocus();
}
bool ExprWnd::isEditing() const
{
return m_edit != 0 && m_edit->isVisible();
}
ValueEdit::ValueEdit(ExprWnd* parent) :
QLineEdit(parent->viewport())
{
setFrame(false);
hide();
lower(); // lower the window below scrollbars
connect(parent, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
SLOT(slotSelectionChanged()));
connect(parent, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
SLOT(slotSelectionChanged()));
connect(parent, SIGNAL(itemExpanded(QTreeWidgetItem*)),
SLOT(slotSelectionChanged()));
connect(parent, SIGNAL(itemCollapsed(QTreeWidgetItem*)),
SLOT(slotSelectionChanged()));
connect(this, SIGNAL(done(VarTree*, const QString&)),
parent, SIGNAL(editValueCommitted(VarTree*, const QString&)));
}
ValueEdit::~ValueEdit()
{
}
void ValueEdit::terminate(bool commit)
{
TRACE(commit?"ValueEdit::terminate(true)":"ValueEdit::terminate(false)");
if (!m_finished)
{
m_finished = true;
hide(); // will call focusOutEvent, that's why we need m_finished
if (commit) {
emit done(m_item, text());
}
}
}
void ValueEdit::keyPressEvent(QKeyEvent *e)
{
if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)
terminate(true);
else if(e->key() == Qt::Key_Escape)
terminate(false);
else
QLineEdit::keyPressEvent(e);
}
void ValueEdit::paintEvent(QPaintEvent* e)
{
QLineEdit::paintEvent(e);
QPainter p(this);
p.drawRect(rect());
}
void ValueEdit::focusOutEvent(QFocusEvent* ev)
{
TRACE("ValueEdit::focusOutEvent");
QFocusEvent* focusEv = static_cast(ev);
if (focusEv->reason() == Qt::ActiveWindowFocusReason)
{
// Switching to a different window should terminate the edit,
// because if the window with this variable display is floating
// then that different window could be the main window, where
// the user had clicked one of the Execute buttons. This in turn
// may pull the item away that we are editing here.
terminate(false);
}
// Don't let a RMB close the editor
else if (focusEv->reason() != Qt::PopupFocusReason)
{
terminate(true);
}
}
void ValueEdit::slotSelectionChanged()
{
TRACE("ValueEdit::slotSelectionChanged");
terminate(false);
}
kdbg-2.5.4/kdbg/exprwnd.h 0000664 0000000 0000000 00000014261 12237506721 0015211 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#ifndef EXPRWND_H
#define EXPRWND_H
#include
#include
#include
#include
class ProgramTypeTable;
class TypeInfo;
struct ExprValue;
class ExprWnd;
class QStringList;
/*! \brief a variable's value is the tree of sub-variables */
class VarTree : public QTreeWidgetItem
{
public:
enum VarKind { VKsimple, VKpointer, VKstruct, VKarray,
VKdummy //!< used to update only children
};
VarKind m_varKind;
enum NameKind { NKplain, NKstatic, NKtype,
NKanonymous, //!< an anonymous struct or union
NKaddress //!< a dereferenced pointer
};
NameKind m_nameKind;
const TypeInfo* m_type; //!< the type of struct if it could be derived
int m_exprIndex; //!< used in struct value update
bool m_exprIndexUseGuard; //!< ditto; if guard expr should be used
QString m_partialValue; //!< while struct value update is in progress
VarTree(VarTree* parent, ExprValue* v);
VarTree(ExprWnd* parent, ExprValue* v);
virtual ~VarTree();
public:
QString computeExpr() const;
bool isToplevelExpr() const;
/** is this element an ancestor of (or equal to) child? */
bool isAncestorEq(const VarTree* child) const;
/** update the regular value; returns whether a repaint is necessary */
bool updateValue(const QString& newValue);
/** update the "quick member" value; returns whether repaint is necessary */
bool updateStructValue(const QString& newValue);
/** find out the type of this value using the child values */
void inferTypesOfChildren(ProgramTypeTable& typeTable);
/** get the type from base class part */
const TypeInfo* inferTypeFromBaseClass();
/** returns whether the pointer is a wchar_t */
bool isWcharT() const;
QString getText() const { return text(0); }
using QTreeWidgetItem::setText;
void setText(const QString& t) { QTreeWidgetItem::setText(0, t); }
void setPixmap(const QPixmap& p) { QTreeWidgetItem::setIcon(0, QIcon(p)); }
QString value() const { return m_baseValue; }
VarTree* child(int i) const { return static_cast(QTreeWidgetItem::child(i)); }
private:
void updateValueText();
QString m_baseValue; //!< The "normal value" that the driver reported
QString m_structValue; //!< The "quick member" value
bool m_baseChanged : 1;
bool m_structChanged : 1;
};
/**
* Represents the value tree that is parsed by the debugger drivers.
*/
struct ExprValue
{
QString m_name;
QString m_value;
VarTree::VarKind m_varKind;
VarTree::NameKind m_nameKind;
ExprValue* m_child; /* the first child expression */
ExprValue* m_next; /* the next sibling expression */
bool m_initiallyExpanded;
ExprValue(const QString& name, VarTree::NameKind kind);
~ExprValue();
void appendChild(ExprValue* newChild);
int childCount() const;
};
class ValueEdit : public QLineEdit
{
Q_OBJECT
public:
ValueEdit(ExprWnd* parent);
~ValueEdit();
void terminate(bool commit);
VarTree* m_item;
bool m_finished;
protected:
void keyPressEvent(QKeyEvent *e);
void focusOutEvent(QFocusEvent* ev);
void paintEvent(QPaintEvent* e);
public slots:
void slotSelectionChanged();
signals:
void done(VarTree*, const QString&);
};
class ExprWnd : public QTreeWidget
{
Q_OBJECT
public:
ExprWnd(QWidget* parent, const QString& colHeader);
~ExprWnd();
/** returns the list with the expressions at the topmost level */
QStringList exprList() const;
/** appends a copy of expr to the end of the tree at the topmost level;
* returns a pointer to the inserted top-level item */
VarTree* insertExpr(ExprValue* expr, ProgramTypeTable& typeTable);
/** updates an existing expression */
void updateExpr(ExprValue* expr, ProgramTypeTable& typeTable);
void updateExpr(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable);
/** updates the value and repaints it for a single item (not the children) */
void updateSingleExpr(VarTree* display, ExprValue* newValues);
/** updates only the value of the node */
void updateStructValue(VarTree* display);
/** get a top-level expression by name */
VarTree* topLevelExprByName(const QString& name) const;
/** return a member of the struct that pointer \a v refers to */
static VarTree* ptrMemberByName(VarTree* v, const QString& name);
/** return a member of the struct \a v */
static VarTree* memberByName(VarTree* v, const QString& name);
/** removes an expression; must be on the topmost level*/
void removeExpr(VarTree* item);
/** clears the list of pointers needing updates */
void clearPendingUpdates();
/** returns a pointer to update (or 0) and removes it from the list */
VarTree* nextUpdatePtr();
VarTree* nextUpdateType();
VarTree* nextUpdateStruct();
void editValue(VarTree* item, const QString& text);
/** tells whether the a value is currently edited */
bool isEditing() const;
VarTree* selectedItem() const { return static_cast(QTreeWidget::currentItem()); }
VarTree* topLevelItem(int i) const { return static_cast(QTreeWidget::topLevelItem(i)); }
protected:
void updateExprRec(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable);
void replaceChildren(VarTree* display, ExprValue* newValues);
void collectUnknownTypes(VarTree* item);
void checkUnknownType(VarTree* item);
static QString formatWCharPointer(QString value);
QPixmap m_pixPointer;
std::list m_updatePtrs; //!< dereferenced pointers that need update
std::list m_updateType; //!< structs whose type must be determined
std::list m_updateStruct; //!< structs whose nested value needs update
ValueEdit* m_edit;
/** remove items that are in the subTree from the list */
void unhookSubtree(VarTree* subTree);
static void unhookSubtree(std::list& list, VarTree* subTree);
signals:
void removingItem(VarTree*);
void editValueCommitted(VarTree*, const QString&);
};
#endif // EXPRWND_H
kdbg-2.5.4/kdbg/gdbdriver.cpp 0000664 0000000 0000000 00000214605 12237506721 0016031 0 ustar 00root root 0000000 0000000 /*
* Copyright Johannes Sixt
* This file is licensed under the GNU General Public License Version 2.
* See the file COPYING in the toplevel directory of the source directory.
*/
#include "gdbdriver.h"
#include "exprwnd.h"
#include
#include
#include
#include /* i18n */
#include
#include
#include /* strtol, atoi */
#include /* strcpy */
#include "assert.h"
#include "mydebug.h"
static void skipString(const char*& p);
static void skipNested(const char*& s, char opening, char closing);
static ExprValue* parseVar(const char*& s);
static bool parseName(const char*& s, QString& name, VarTree::NameKind& kind);
static bool parseValue(const char*& s, ExprValue* variable);
static bool parseNested(const char*& s, ExprValue* variable);
static bool parseVarSeq(const char*& s, ExprValue* variable);
static bool parseValueSeq(const char*& s, ExprValue* variable);
#define PROMPT "(kdbg)"
#define PROMPT_LEN 6
// TODO: make this cmd info stuff non-static to allow multiple
// simultaneous gdbs to run!
struct GdbCmdInfo {
DbgCommand cmd;
const char* fmt; /* format string */
enum Args {
argNone, argString, argNum,
argStringNum, argNumString,
argString2, argNum2
} argsNeeded;
};
#if 0
// This is how the QString data print statement generally looks like.
// It is set by KDebugger via setPrintQStringDataCmd().
static const char printQStringStructFmt[] =
// if the string data is junk, fail early
"print ($qstrunicode=($qstrdata=(%s))->unicode)?"
// print an array of shorts
"(*(unsigned short*)$qstrunicode)@"
// limit the length
"(($qstrlen=(unsigned int)($qstrdata->len))>100?100:$qstrlen)"
// if unicode data is 0, report a special value
":1==0\n";
#endif
static const char printQStringStructFmt[] = "print (0?\"%s\":$kdbgundef)\n";
/*
* The following array of commands must be sorted by the DC* values,
* because they are used as indices.
*/
static GdbCmdInfo cmds[] = {
{ DCinitialize, "", GdbCmdInfo::argNone },
{ DCtty, "tty %s\n", GdbCmdInfo::argString },
{ DCexecutable, "file \"%s\"\n", GdbCmdInfo::argString },
{ DCtargetremote, "target remote %s\n", GdbCmdInfo::argString },
#ifdef __FreeBSD__
{ DCcorefile, "target FreeBSD-core %s\n", GdbCmdInfo::argString },
#else
{ DCcorefile, "target core %s\n", GdbCmdInfo::argString },
#endif
{ DCattach, "attach %s\n", GdbCmdInfo::argString },
{ DCinfolinemain, "kdbg_infolinemain\n", GdbCmdInfo::argNone },
{ DCinfolocals, "kdbg__alllocals\n", GdbCmdInfo::argNone },
{ DCinforegisters, "info all-registers\n", GdbCmdInfo::argNone},
{ DCexamine, "x %s %s\n", GdbCmdInfo::argString2 },
{ DCinfoline, "info line %s:%d\n", GdbCmdInfo::argStringNum },
{ DCdisassemble, "disassemble %s %s\n", GdbCmdInfo::argString2 },
{ DCsetargs, "set args %s\n", GdbCmdInfo::argString },
{ DCsetenv, "set env %s %s\n", GdbCmdInfo::argString2 },
{ DCunsetenv, "unset env %s\n", GdbCmdInfo::argString },
{ DCsetoption, "setoption %s %d\n", GdbCmdInfo::argStringNum},
{ DCcd, "cd %s\n", GdbCmdInfo::argString },
{ DCbt, "bt\n", GdbCmdInfo::argNone },
{ DCrun, "run\n", GdbCmdInfo::argNone },
{ DCcont, "cont\n", GdbCmdInfo::argNone },
{ DCstep, "step\n", GdbCmdInfo::argNone },
{ DCstepi, "stepi\n", GdbCmdInfo::argNone },
{ DCnext, "next\n", GdbCmdInfo::argNone },
{ DCnexti, "nexti\n", GdbCmdInfo::argNone },
{ DCfinish, "finish\n", GdbCmdInfo::argNone },
{ DCuntil, "until %s:%d\n", GdbCmdInfo::argStringNum },
{ DCkill, "kill\n", GdbCmdInfo::argNone },
{ DCbreaktext, "break %s\n", GdbCmdInfo::argString },
{ DCbreakline, "break %s:%d\n", GdbCmdInfo::argStringNum },
{ DCtbreakline, "tbreak %s:%d\n", GdbCmdInfo::argStringNum },
{ DCbreakaddr, "break *%s\n", GdbCmdInfo::argString },
{ DCtbreakaddr, "tbreak *%s\n", GdbCmdInfo::argString },
{ DCwatchpoint, "watch %s\n", GdbCmdInfo::argString },
{ DCdelete, "delete %d\n", GdbCmdInfo::argNum },
{ DCenable, "enable %d\n", GdbCmdInfo::argNum },
{ DCdisable, "disable %d\n", GdbCmdInfo::argNum },
{ DCprint, "print %s\n", GdbCmdInfo::argString },
{ DCprintDeref, "print *(%s)\n", GdbCmdInfo::argString },
{ DCprintStruct, "print %s\n", GdbCmdInfo::argString },
{ DCprintQStringStruct, printQStringStructFmt, GdbCmdInfo::argString},
{ DCframe, "frame %d\n", GdbCmdInfo::argNum },
{ DCfindType, "whatis %s\n", GdbCmdInfo::argString },
{ DCinfosharedlib, "info sharedlibrary\n", GdbCmdInfo::argNone },
{ DCthread, "thread %d\n", GdbCmdInfo::argNum },
{ DCinfothreads, "info threads\n", GdbCmdInfo::argNone },
{ DCinfobreak, "info breakpoints\n", GdbCmdInfo::argNone },
{ DCcondition, "condition %d %s\n", GdbCmdInfo::argNumString},
{ DCsetpc, "set variable $pc=%s\n", GdbCmdInfo::argString },
{ DCignore, "ignore %d %d\n", GdbCmdInfo::argNum2},
{ DCprintWChar, "print ($s=%s)?*$s@wcslen($s):0x0\n", GdbCmdInfo::argString },
{ DCsetvariable, "set variable %s=%s\n", GdbCmdInfo::argString2 },
};
#define NUM_CMDS (int(sizeof(cmds)/sizeof(cmds[0])))
#define MAX_FMTLEN 200
GdbDriver::GdbDriver() :
DebuggerDriver()
{
#ifndef NDEBUG
// check command info array
const char* perc;
for (int i = 0; i < NUM_CMDS; i++) {
// must be indexable by DbgCommand values, i.e. sorted by DbgCommand values
assert(i == cmds[i].cmd);
// a format string must be associated
assert(cmds[i].fmt != 0);
assert(strlen(cmds[i].fmt) <= MAX_FMTLEN);
// format string must match arg specification
switch (cmds[i].argsNeeded) {
case GdbCmdInfo::argNone:
assert(strchr(cmds[i].fmt, '%') == 0);
break;
case GdbCmdInfo::argString:
perc = strchr(cmds[i].fmt, '%');
assert(perc != 0 && perc[1] == 's');
assert(strchr(perc+2, '%') == 0);
break;
case GdbCmdInfo::argNum:
perc = strchr(cmds[i].fmt, '%');
assert(perc != 0 && perc[1] == 'd');
assert(strchr(perc+2, '%') == 0);
break;
case GdbCmdInfo::argStringNum:
perc = strchr(cmds[i].fmt, '%');
assert(perc != 0 && perc[1] == 's');
perc = strchr(perc+2, '%');
assert(perc != 0 && perc[1] == 'd');
assert(strchr(perc+2, '%') == 0);
break;
case GdbCmdInfo::argNumString:
perc = strchr(cmds[i].fmt, '%');
assert(perc != 0 && perc[1] == 'd');
perc = strchr(perc+2, '%');
assert(perc != 0 && perc[1] == 's');
assert(strchr(perc+2, '%') == 0);
break;
case GdbCmdInfo::argString2:
perc = strchr(cmds[i].fmt, '%');
assert(perc != 0 && perc[1] == 's');
perc = strchr(perc+2, '%');
assert(perc != 0 && perc[1] == 's');
assert(strchr(perc+2, '%') == 0);
break;
case GdbCmdInfo::argNum2:
perc = strchr(cmds[i].fmt, '%');
assert(perc != 0 && perc[1] == 'd');
perc = strchr(perc+2, '%');
assert(perc != 0 && perc[1] == 'd');
assert(strchr(perc+2, '%') == 0);
break;
}
}
assert(strlen(printQStringStructFmt) <= MAX_FMTLEN);
#endif
}
GdbDriver::~GdbDriver()
{
}
QString GdbDriver::driverName() const
{
return "GDB";
}
QString GdbDriver::defaultGdb()
{
return
"gdb"
" --fullname" /* to get standard file names each time the prog stops */
" --nx"; /* do not execute initialization files */
}
QString GdbDriver::defaultInvocation() const
{
if (m_defaultCmd.isEmpty()) {
return defaultGdb();
} else {
return m_defaultCmd;
}
}
QStringList GdbDriver::boolOptionList() const
{
// no options
return QStringList();
}
bool GdbDriver::startup(QString cmdStr)
{
if (!DebuggerDriver::startup(cmdStr))
return false;
static const char gdbInitialize[] =
/*
* Work around buggy gdbs that do command line editing even if they
* are not on a tty. The readline library echos every command back
* in this case, which is confusing for us.
*/
"set editing off\n"
"set confirm off\n"
"set print static-members off\n"
"set print asm-demangle on\n"
/*
* Sometimes, gdb prints [New Thread ...] during 'info threads';
* we will not look at thread events anyway, so turn them off.
*/
"set print thread-events off\n"
/*
* Don't assume that program functions invoked from a watch expression
* always succeed.
*/
"set unwindonsignal on\n"
/*
* Write a short macro that prints all locals: local variables and
* function arguments.
*/
"define kdbg__alllocals\n"
"info locals\n" /* local vars supersede args with same name */
"info args\n" /* therefore, arguments must come last */
"end\n"
/*
* Work around a bug in gdb-6.3: "info line main" crashes gdb.
*/
"define kdbg_infolinemain\n"
"list\n"
"info line\n"
"end\n"
// change prompt string and synchronize with gdb
"set prompt " PROMPT "\n"
;
executeCmdString(DCinitialize, gdbInitialize, false);
// assume that QString::null is ok
cmds[DCprintQStringStruct].fmt = printQStringStructFmt;
return true;
}
void GdbDriver::commandFinished(CmdQueueItem* cmd)
{
// command string must be committed
if (!cmd->m_committed) {
// not commited!
TRACE("calling " + (__PRETTY_FUNCTION__ + (" with uncommited command:\n\t" +
cmd->m_cmdString)));
return;
}
switch (cmd->m_cmd) {
case DCinitialize:
{
/*
* Check for GDB 7.1 or later; the syntax for the disassemble
* command has changed.
* This RE picks the last version number in the first line,
* because at least OpenSUSE writes its own version number
* in the first line (but before GDB's version number).
*/
QRegExp re(
" " // must be preceded by space
"[(]?" // SLES 10 embeds in parentheses
"(\\d+)\\.(\\d+)" // major, minor
"[^ ]*\\n" // no space until end of line
);
int pos = re.indexIn(m_output);
const char* disass = "disassemble %s %s\n";
if (pos >= 0) {
int major = re.cap(1).toInt();
int minor = re.cap(2).toInt();
if (major > 7 || (major == 7 && minor >= 1))
{
disass = "disassemble %s, %s\n";
}
}
cmds[DCdisassemble].fmt = disass;
}
break;
default:;
}
/* ok, the command is ready */
emit commandReceived(cmd, m_output.constData());
switch (cmd->m_cmd) {
case DCcorefile:
case DCinfolinemain:
case DCinfoline:
case DCframe:
case DCattach:
case DCrun:
case DCcont:
case DCstep:
case DCstepi:
case DCnext:
case DCnexti:
case DCfinish:
case DCuntil:
parseMarker(cmd);
default:;
}
}
int GdbDriver::findPrompt(const QByteArray& output) const
{
/*
* If there's a prompt string in the collected output, it must be at
* the very end.
*
* Note: It could nevertheless happen that a character sequence that is
* equal to the prompt string appears at the end of the output,
* although it is very, very unlikely (namely as part of a string that
* lingered in gdb's output buffer due to some timing/heavy load
* conditions for a very long time such that that buffer overflowed
* exactly at the end of the prompt string look-a-like).
*/
int len = output.length();
if (len >= PROMPT_LEN &&
strncmp(output.data()+len-PROMPT_LEN, PROMPT, PROMPT_LEN) == 0)
{
return len-PROMPT_LEN;
}
return -1;
}
/*
* The --fullname option makes gdb send a special normalized sequence print
* each time the program stops and at some other points. The sequence has
* the form "\032\032filename:lineno:charoffset:(beg|middle):address".
*/
void GdbDriver::parseMarker(CmdQueueItem* cmd)
{
char* startMarker = strstr(m_output.data(), "\032\032");
if (startMarker == 0)
return;
// extract the marker
startMarker += 2;
TRACE(QString("found marker: ") + startMarker);
char* endMarker = strchr(startMarker, '\n');
if (endMarker == 0)
return;
*endMarker = '\0';
// extract filename and line number
static QRegExp MarkerRE(":(\\d+):\\d+:[begmidl]+:0x");
int lineNoStart = MarkerRE.indexIn(startMarker);
if (lineNoStart >= 0) {
int lineNo = MarkerRE.cap(1).toInt();
// get address unless there is one in cmd
DbgAddr address = cmd->m_addr;
if (address.isEmpty()) {
const char* addrStart = startMarker + lineNoStart +
MarkerRE.matchedLength() - 2;
address = QString(addrStart).trimmed();
}
// now show the window
startMarker[lineNoStart] = '\0'; /* split off file name */
emit activateFileLine(startMarker, lineNo-1, address);
}
}
/*
* Escapes characters that might lead to problems when they appear on gdb's
* command line.
*/
static void normalizeStringArg(QString& arg)
{
/*
* Remove trailing backslashes. This approach is a little simplistic,
* but we know that there is at the moment no case where a trailing
* backslash would make sense.
*/
while (!arg.isEmpty() && arg[arg.length()-1] == '\\') {
arg = arg.left(arg.length()-1);
}
}
QString GdbDriver::makeCmdString(DbgCommand cmd, QString strArg)
{
assert(cmd >= 0 && cmd < NUM_CMDS);
assert(cmds[cmd].argsNeeded == GdbCmdInfo::argString);
normalizeStringArg(strArg);
if (cmd == DCcd) {
// need the working directory when parsing the output
m_programWD = strArg;
} else if (cmd == DCsetargs && !m_redirect.isEmpty()) {
/*
* Use saved redirection. We prepend it in front of the user's
* arguments so that the user can override the redirections.
*/
strArg = m_redirect + " " + strArg;
}
QString cmdString;
cmdString.sprintf(cmds[cmd].fmt, strArg.toUtf8().constData());
return cmdString;
}
QString GdbDriver::makeCmdString(DbgCommand cmd, int intArg)
{
assert(cmd >= 0 && cmd < NUM_CMDS);
assert(cmds[cmd].argsNeeded == GdbCmdInfo::argNum);
QString cmdString;
cmdString.sprintf(cmds[cmd].fmt, intArg);
return cmdString;
}
QString GdbDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg)
{
assert(cmd >= 0 && cmd < NUM_CMDS);
assert(cmds[cmd].argsNeeded == GdbCmdInfo::argStringNum ||
cmds[cmd].argsNeeded == GdbCmdInfo::argNumString ||
cmd == DCexamine ||
cmd == DCtty);
normalizeStringArg(strArg);
QString cmdString;
if (cmd == DCtty)
{
/*
* intArg specifies which channels should be redirected to
* /dev/null. It is a value or'ed together from RDNstdin,
* RDNstdout, RDNstderr. We store the value for a later DCsetargs
* command.
*
* Note: We rely on that after the DCtty a DCsetargs will follow,
* which will ultimately apply the redirection.
*/
static const char* const runRedir[8] = {
"",
"/dev/null",
"/dev/null",
"2>/dev/null",
"/dev/null",
">/dev/null 2>&1",
"/dev/null 2>&1"
};
if (strArg.isEmpty())
intArg = 7; /* failsafe if no tty */
m_redirect = runRedir[intArg & 7];
return makeCmdString(DCtty, strArg); /* note: no problem if strArg empty */
}
if (cmd == DCexamine) {
// make a format specifier from the intArg
static const char size[16] = {
'\0', 'b', 'h', 'w', 'g'
};
static const char format[16] = {
'\0', 'x', 'd', 'u', 'o', 't',
'a', 'c', 'f', 's', 'i'
};
assert(MDTsizemask == 0xf); /* lowest 4 bits */
assert(MDTformatmask == 0xf0); /* next 4 bits */
int count = 16; /* number of entities to print */
char sizeSpec = size[intArg & MDTsizemask];
char formatSpec = format[(intArg & MDTformatmask) >> 4];
assert(sizeSpec != '\0');
assert(formatSpec != '\0');
// adjust count such that 16 lines are printed
switch (intArg & MDTformatmask) {
case MDTstring: case MDTinsn:
break; /* no modification needed */
default:
// all cases drop through:
switch (intArg & MDTsizemask) {
case MDTbyte:
case MDThalfword:
count *= 2;
case MDTword:
count *= 2;
case MDTgiantword:
count *= 2;
}
break;
}
QString spec;
spec.sprintf("/%d%c%c", count, sizeSpec, formatSpec);
return makeCmdString(DCexamine, spec, strArg);
}
if (cmds[cmd].argsNeeded == GdbCmdInfo::argStringNum)
{
// line numbers are zero-based
if (cmd == DCuntil || cmd == DCbreakline ||
cmd == DCtbreakline || cmd == DCinfoline)
{
intArg++;
}
if (cmd == DCinfoline)
{
// must split off file name part
strArg = QFileInfo(strArg).fileName();
}
cmdString.sprintf(cmds[cmd].fmt, strArg.toUtf8().constData(), intArg);
}
else
{
cmdString.sprintf(cmds[cmd].fmt, intArg, strArg.toUtf8().constData());
}
return cmdString;
}
QString GdbDriver::makeCmdString(DbgCommand cmd, QString strArg1, QString strArg2)
{
assert(cmd >= 0 && cmd < NUM_CMDS);
assert(cmds[cmd].argsNeeded == GdbCmdInfo::argString2);
normalizeStringArg(strArg1);
normalizeStringArg(strArg2);
QString cmdString;
cmdString.sprintf(cmds[cmd].fmt,
strArg1.toUtf8().constData(),
strArg2.toUtf8().constData());
return cmdString;
}
QString GdbDriver::makeCmdString(DbgCommand cmd, int intArg1, int intArg2)
{
assert(cmd >= 0 && cmd < NUM_CMDS);
assert(cmds[cmd].argsNeeded == GdbCmdInfo::argNum2);
QString cmdString;
cmdString.sprintf(cmds[cmd].fmt, intArg1, intArg2);
return cmdString;
}
CmdQueueItem* GdbDriver::executeCmd(DbgCommand cmd, bool clearLow)
{
assert(cmd >= 0 && cmd < NUM_CMDS);
assert(cmds[cmd].argsNeeded == GdbCmdInfo::argNone);
if (cmd == DCrun) {
m_haveCoreFile = false;
}
return executeCmdString(cmd, cmds[cmd].fmt, clearLow);
}
CmdQueueItem* GdbDriver::executeCmd(DbgCommand cmd, QString strArg,
bool clearLow)
{
return executeCmdString(cmd, makeCmdString(cmd, strArg), clearLow);
}
CmdQueueItem* GdbDriver::executeCmd(DbgCommand cmd, int intArg,
bool clearLow)
{
return executeCmdString(cmd, makeCmdString(cmd, intArg), clearLow);
}
CmdQueueItem* GdbDriver::executeCmd(DbgCommand cmd, QString strArg, int intArg,
bool clearLow)
{
return executeCmdString(cmd, makeCmdString(cmd, strArg, intArg), clearLow);
}
CmdQueueItem* GdbDriver::executeCmd(DbgCommand cmd, QString strArg1, QString strArg2,
bool clearLow)
{
return executeCmdString(cmd, makeCmdString(cmd, strArg1, strArg2), clearLow);
}
CmdQueueItem* GdbDriver::executeCmd(DbgCommand cmd, int intArg1, int intArg2,
bool clearLow)
{
return executeCmdString(cmd, makeCmdString(cmd, intArg1, intArg2), clearLow);
}
CmdQueueItem* GdbDriver::queueCmd(DbgCommand cmd, QueueMode mode)
{
return queueCmdString(cmd, cmds[cmd].fmt, mode);
}
CmdQueueItem* GdbDriver::queueCmd(DbgCommand cmd, QString strArg,
QueueMode mode)
{
return queueCmdString(cmd, makeCmdString(cmd, strArg), mode);
}
CmdQueueItem* GdbDriver::queueCmd(DbgCommand cmd, int intArg,
QueueMode mode)
{
return queueCmdString(cmd, makeCmdString(cmd, intArg), mode);
}
CmdQueueItem* GdbDriver::queueCmd(DbgCommand cmd, QString strArg, int intArg,
QueueMode mode)
{
return queueCmdString(cmd, makeCmdString(cmd, strArg, intArg), mode);
}
CmdQueueItem* GdbDriver::queueCmd(DbgCommand cmd, QString strArg1, QString strArg2,
QueueMode mode)
{
return queueCmdString(cmd, makeCmdString(cmd, strArg1, strArg2), mode);
}
void GdbDriver::terminate()
{
::kill(pid(), SIGTERM);
m_state = DSidle;
}
void GdbDriver::detachAndTerminate()
{
::kill(pid(), SIGINT);
flushCommands();
executeCmdString(DCinitialize, "detach\nquit\n", true);
}
void GdbDriver::interruptInferior()
{
::kill(pid(), SIGINT);
// remove accidentally queued commands
flushHiPriQueue();
}
static bool isErrorExpr(const char* output)
{
return
strncmp(output, "Cannot access memory at", 23) == 0 ||
strncmp(output, "Attempt to dereference a generic pointer", 40) == 0 ||
strncmp(output, "Attempt to take contents of ", 28) == 0 ||
strncmp(output, "Attempt to use a type name as an expression", 43) == 0 ||
strncmp(output, "There is no member or method named", 34) == 0 ||
strncmp(output, "A parse error in expression", 27) == 0 ||
strncmp(output, "No symbol \"", 11) == 0 ||
strncmp(output, "Internal error: ", 16) == 0;
}
/**
* Returns true if the output is an error message. If wantErrorValue is
* true, a new ExprValue object is created and filled with the error message.
* If there are warnings, they are skipped and output points past the warnings
* on return (even if there \e are errors).
*/
static bool parseErrorMessage(const char*& output,
ExprValue*& variable, bool wantErrorValue)
{
while (isspace(*output))
output++;
// skip warnings
while (strncmp(output, "warning:", 8) == 0)
{
const char* end = strchr(output+8, '\n');
if (end == 0)
output += strlen(output);
else
output = end+1;
while (isspace(*output))
output++;
}
if (isErrorExpr(output))
{
if (wantErrorValue) {
// put the error message as value in the variable
variable = new ExprValue(QString(), VarTree::NKplain);
const char* endMsg = strchr(output, '\n');
if (endMsg == 0)
endMsg = output + strlen(output);
variable->m_value = QString::fromLatin1(output, endMsg-output);
} else {
variable = 0;
}
return true;
}
return false;
}
#if QT_VERSION >= 300
union Qt2QChar {
short s;
struct {
uchar row;
uchar cell;
} qch;
};
#endif
void GdbDriver::setPrintQStringDataCmd(const char* cmd)
{
// don't accept the command if it is empty
if (cmd == 0 || *cmd == '\0')
return;
assert(strlen(cmd) <= MAX_FMTLEN);
cmds[DCprintQStringStruct].fmt = cmd;
}
ExprValue* GdbDriver::parseQCharArray(const char* output, bool wantErrorValue, bool qt3like)
{
ExprValue* variable = 0;
/*
* Parse off white space. gdb sometimes prints white space first if the
* printed array leaded to an error.
*/
while (isspace(*output))
output++;
// special case: empty string (0 repetitions)
if (strncmp(output, "Invalid number 0 of repetitions", 31) == 0)
{
variable = new ExprValue(QString(), VarTree::NKplain);
variable->m_value = "\"\"";
return variable;
}
// check for error conditions
if (parseErrorMessage(output, variable, wantErrorValue))
return variable;
// parse the array
// find '='
const char* p = output;
p = strchr(p, '=');
if (p == 0) {
goto error;
}
// skip white space
do {
p++;
} while (isspace(*p));
if (*p == '{')
{
// this is the real data
p++; /* skip '{' */
// parse the array
QString result;
QString repeatCount;
enum { wasNothing, wasChar, wasRepeat } lastThing = wasNothing;
/*
* A matrix for separators between the individual "things"
* that are added to the string. The first index is a bool,
* the second index is from the enum above.
*/
static const char* separator[2][3] = {
{ "\"", 0, ", \"" }, /* normal char is added */
{ "'", "\", '", ", '" } /* repeated char is added */
};
while (isdigit(*p)) {
// parse a number
char* end;
unsigned short value = (unsigned short) strtoul(p, &end, 0);
if (end == p)
goto error; /* huh? no valid digits */
// skip separator and search for a repeat count
p = end;
while (isspace(*p) || *p == ',')
p++;
bool repeats = strncmp(p, "'); /* search end and advance */
if (p == 0)
goto error;
p++; /* skip '>' */
repeatCount = QString::fromLatin1(start, p-start);
while (isspace(*p) || *p == ',')
p++;
}
// p is now at the next char (or the end)
// interpret the value as a QChar
// TODO: make cross-architecture compatible
QChar ch;
if (qt3like) {
ch = QChar(value);
} else {
#if QT_VERSION < 300
(unsigned short&)ch = value;
#else
Qt2QChar c;
c.s = value;
ch.setRow(c.qch.row);
ch.setCell(c.qch.cell);
#endif
}
// escape a few frequently used characters
char escapeCode = '\0';
switch (ch.toLatin1()) {
case '\n': escapeCode = 'n'; break;
case '\r': escapeCode = 'r'; break;
case '\t': escapeCode = 't'; break;
case '\b': escapeCode = 'b'; break;
case '\"': escapeCode = '\"'; break;
case '\\': escapeCode = '\\'; break;
case '\0': if (value == 0) { escapeCode = '0'; } break;
}
// add separator
result += separator[repeats][lastThing];
// add char
if (escapeCode != '\0') {
result += '\\';
ch = escapeCode;
}
result += ch;
// fixup repeat count and lastThing
if (repeats) {
result += "' ";
result += repeatCount;
lastThing = wasRepeat;
} else {
lastThing = wasChar;
}
}
if (*p != '}')
goto error;
// closing quote
if (lastThing == wasChar)
result += "\"";
// assign the value
variable = new ExprValue(QString(), VarTree::NKplain);
variable->m_value = result;
}
else if (strncmp(p, "true", 4) == 0)
{
variable = new ExprValue(QString(), VarTree::NKplain);
variable->m_value = "QString::null";
}
else if (strncmp(p, "false", 5) == 0)
{
variable = new ExprValue(QString(), VarTree::NKplain);
variable->m_value = "(null)";
}
else
goto error;
return variable;
error:
if (wantErrorValue) {
variable = new ExprValue(QString(), VarTree::NKplain);
variable->m_value = "internal parse error";
}
return variable;
}
static ExprValue* parseVar(const char*& s)
{
const char* p = s;
// skip whitespace
while (isspace(*p))
p++;
QString name;
VarTree::NameKind kind;
/*
* Detect anonymouse struct values: The 'name =' part is missing:
* s = { a = 1, { b = 2 }}
* Note that this detection works only inside structs when the anonymous
* struct is not the first member:
* s = {{ a = 1 }, b = 2}
* This is misparsed (by parseNested()) because it is mistakenly
* interprets the second opening brace as the first element of an array
* of structs.
*/
if (*p == '{')
{
name = i18n("");
kind = VarTree::NKanonymous;
}
else
{
if (!parseName(p, name, kind)) {
return 0;
}
// go for '='
while (isspace(*p))
p++;
if (*p != '=') {
TRACE("parse error: = not found after " + name);
return 0;
}
// skip the '=' and more whitespace
p++;
while (isspace(*p))
p++;
}
ExprValue* variable = new ExprValue(name, kind);
if (!parseValue(p, variable)) {
delete variable;
return 0;
}
s = p;
return variable;
}
static void skipNested(const char*& s, char opening, char closing)
{
const char* p = s;
// parse a nested type
int nest = 1;
p++;
/*
* Search for next matching `closing' char, skipping nested pairs of
* `opening' and `closing'.
*/
while (*p && nest > 0) {
if (*p == opening) {
nest++;
} else if (*p == closing) {
nest--;
}
p++;
}
if (nest != 0) {
TRACE(QString().sprintf("parse error: mismatching %c%c at %-20.20s", opening, closing, s));
}
s = p;
}
/**
* This function skips text that is delimited by nested angle bracktes, '<>'.
* A complication arises because the delimited text can contain the names of
* operator<<, operator>>, operator<, and operator>, which have to be treated
* specially so that they do not count towards the nesting of '<>'.
* This function assumes that the delimited text does not contain strings.
*/
static void skipNestedAngles(const char*& s)
{
const char* p = s;
int nest = 1;
p++; // skip the initial '<'
while (*p && nest > 0)
{
// Below we can check for p-s >= 9 instead of 8 because
// *s is '<' and cannot be part of "operator".
if (*p == '<')
{
if (p-s >= 9 && strncmp(p-8, "operator", 8) == 0) {
if (p[1] == '<')
p++;
} else {
nest++;
}
}
else if (*p == '>')
{
if (p-s >= 9 && strncmp(p-8, "operator", 8) == 0) {
if (p[1] == '>')
p++;
} else {
nest--;
}
}
p++;
}
if (nest != 0) {
TRACE(QString().sprintf("parse error: mismatching <> at %-20.20s", s));
}
s = p;
}
/**
* Find the end of line that is not inside braces
*/
static void findEnd(const char*& s)
{
const char* p = s;
while (*p && *p!='\n') {
while (*p && *p!='\n' && *p!='{')
p++;
if (*p=='{') {
p++;
skipNested(p, '{', '}'); p--;
}
}
s = p;
}
static bool isNumberish(const char ch)
{
return (ch>='0' && ch<='9') || ch=='.' || ch=='x';
}
void skipString(const char*& p)
{
// wchar_t strings begin with L
if (*p == 'L')
++p;
moreStrings:
// opening quote
char quote = *p++;
while (*p != quote) {
if (*p == '\\') {
// skip escaped character
// no special treatment for octal values necessary
p++;
}
// simply return if no more characters
if (*p == '\0')
return;
p++;
}
// closing quote
p++;
/*
* Strings can consist of several parts, some of which contain repeated
* characters.
*/
if (quote == '\'') {
// look ahaead for
const char* q = p+1;
while (isspace(*q))
q++;
if (strncmp(q, "')
p++;
if (*p != '\0') {
p++; /* skip the '>' */
}
}
}
// Is the string continued? If so, there is no L in wchar_t strings
if (*p == ',')
{
// look ahead for another quote
const char* q = p+1;
while (isspace(*q))
q++;
if (*q == '"' || *q == '\'') {
// yes!
p = q;
goto moreStrings;
}
// some strings can end in
if (strncmp(q, "')
p++;
if (*p != '\0') {
p++; /* skip the '>' */
}
}
}
/*
* There's a bug in gdb where it prints the beginning of the string
* continuation and the comma-blank in the wrong order if the new string
* begins with an incomplete multi-byte character. For now, let's check
* for this in a very narrow condition, particularly, where the next
* character is given in octal notation. Example:
* 'a' "\240, b"
*/
if (*p == '"' && p[1] == '\\' && isdigit(p[2])) {
int i = 3;
while (isdigit(p[i]))
++i;
if (p[i] == ',' && p[i+1] == ' ') {
// just treat everything beginning at the dquote as string
goto moreStrings;
}
}
/* very long strings are followed by `...' */
if (*p == '.' && p[1] == '.' && p[2] == '.') {
p += 3;
}
}
static void skipNestedWithString(const char*& s, char opening, char closing)
{
const char* p = s;
// parse a nested expression
int nest = 1;
p++;
/*
* Search for next matching `closing' char, skipping nested pairs of
* `opening' and `closing' as well as strings.
*/
while (*p && nest > 0) {
if (*p == opening) {
nest++;
} else if (*p == closing) {
nest--;
} else if (*p == '\'' || *p == '\"') {
skipString(p);
continue;
}
p++;
}
if (nest > 0) {
TRACE(QString().sprintf("parse error: mismatching %c%c at %-20.20s", opening, closing, s));
}
s = p;
}
inline void skipName(const char*& p)
{
// allow : (for enumeration values) and $ and . (for _vtbl.)
while (isalnum(*p) || *p == '_' || *p == ':' || *p == '$' || *p == '.')
p++;
}
static bool parseName(const char*& s, QString& name, VarTree::NameKind& kind)
{
kind = VarTree::NKplain;
const char* p = s;
// examples of names:
// name
//