mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-23 18:32:51 +00:00
import kcachegrind with automoc migrated
This commit is contained in:
parent
30797fa9c7
commit
c8a5bdd328
174 changed files with 46501 additions and 1 deletions
|
@ -28,3 +28,4 @@ macro_optional_add_subdirectory (kfilereplace)
|
||||||
macro_optional_add_subdirectory (khelpcenter)
|
macro_optional_add_subdirectory (khelpcenter)
|
||||||
macro_optional_add_subdirectory (kaffeine)
|
macro_optional_add_subdirectory (kaffeine)
|
||||||
macro_optional_add_subdirectory (kamoso)
|
macro_optional_add_subdirectory (kamoso)
|
||||||
|
macro_optional_add_subdirectory (kcachegrind)
|
1
kcachegrind/AUTHORS
Normal file
1
kcachegrind/AUTHORS
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
38
kcachegrind/CMakeLists.txt
Normal file
38
kcachegrind/CMakeLists.txt
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
cmake_minimum_required(VERSION 2.6)
|
||||||
|
|
||||||
|
project(kcachegrind)
|
||||||
|
|
||||||
|
find_package(KDE4 4.14.3 REQUIRED)
|
||||||
|
include(KDE4Defaults)
|
||||||
|
include(MacroLibrary)
|
||||||
|
|
||||||
|
set( CMAKE_REQUIRED_DEFINITIONS ${_KDE4_PLATFORM_DEFINITIONS} -DQT_STRICT_ITERATORS )
|
||||||
|
add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES} )
|
||||||
|
|
||||||
|
set( KCACHEGRIND_VERSION "0.7.4kde")
|
||||||
|
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version.h )
|
||||||
|
|
||||||
|
IF(NOT WIN32)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kcachegrind.spec.cmake ${CMAKE_CURRENT_BINARY_DIR}/kcachegrind.spec )
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kcachegrind.lsm.cmake ${CMAKE_CURRENT_BINARY_DIR}/kcachegrind.lsm )
|
||||||
|
ENDIF(NOT WIN32)
|
||||||
|
|
||||||
|
macro_additional_clean_files(
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/version.h
|
||||||
|
)
|
||||||
|
IF(NOT WIN32)
|
||||||
|
macro_additional_clean_files(
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/kcachegrind.lsm
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/kcachegrind.spec
|
||||||
|
)
|
||||||
|
ENDIF(NOT WIN32)
|
||||||
|
|
||||||
|
add_subdirectory( libcore )
|
||||||
|
add_subdirectory( cgview )
|
||||||
|
add_subdirectory( libviews )
|
||||||
|
add_subdirectory( kcachegrind )
|
||||||
|
add_subdirectory( qcachegrind )
|
||||||
|
add_subdirectory( pics )
|
||||||
|
add_subdirectory( converters )
|
340
kcachegrind/COPYING
Normal file
340
kcachegrind/COPYING
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Library General
|
||||||
|
Public License instead of this License.
|
397
kcachegrind/COPYING.DOC
Normal file
397
kcachegrind/COPYING.DOC
Normal file
|
@ -0,0 +1,397 @@
|
||||||
|
GNU Free Documentation License
|
||||||
|
Version 1.2, November 2002
|
||||||
|
|
||||||
|
|
||||||
|
Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
|
||||||
|
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
0. PREAMBLE
|
||||||
|
|
||||||
|
The purpose of this License is to make a manual, textbook, or other
|
||||||
|
functional and useful document "free" in the sense of freedom: to
|
||||||
|
assure everyone the effective freedom to copy and redistribute it,
|
||||||
|
with or without modifying it, either commercially or noncommercially.
|
||||||
|
Secondarily, this License preserves for the author and publisher a way
|
||||||
|
to get credit for their work, while not being considered responsible
|
||||||
|
for modifications made by others.
|
||||||
|
|
||||||
|
This License is a kind of "copyleft", which means that derivative
|
||||||
|
works of the document must themselves be free in the same sense. It
|
||||||
|
complements the GNU General Public License, which is a copyleft
|
||||||
|
license designed for free software.
|
||||||
|
|
||||||
|
We have designed this License in order to use it for manuals for free
|
||||||
|
software, because free software needs free documentation: a free
|
||||||
|
program should come with manuals providing the same freedoms that the
|
||||||
|
software does. But this License is not limited to software manuals;
|
||||||
|
it can be used for any textual work, regardless of subject matter or
|
||||||
|
whether it is published as a printed book. We recommend this License
|
||||||
|
principally for works whose purpose is instruction or reference.
|
||||||
|
|
||||||
|
|
||||||
|
1. APPLICABILITY AND DEFINITIONS
|
||||||
|
|
||||||
|
This License applies to any manual or other work, in any medium, that
|
||||||
|
contains a notice placed by the copyright holder saying it can be
|
||||||
|
distributed under the terms of this License. Such a notice grants a
|
||||||
|
world-wide, royalty-free license, unlimited in duration, to use that
|
||||||
|
work under the conditions stated herein. The "Document", below,
|
||||||
|
refers to any such manual or work. Any member of the public is a
|
||||||
|
licensee, and is addressed as "you". You accept the license if you
|
||||||
|
copy, modify or distribute the work in a way requiring permission
|
||||||
|
under copyright law.
|
||||||
|
|
||||||
|
A "Modified Version" of the Document means any work containing the
|
||||||
|
Document or a portion of it, either copied verbatim, or with
|
||||||
|
modifications and/or translated into another language.
|
||||||
|
|
||||||
|
A "Secondary Section" is a named appendix or a front-matter section of
|
||||||
|
the Document that deals exclusively with the relationship of the
|
||||||
|
publishers or authors of the Document to the Document's overall subject
|
||||||
|
(or to related matters) and contains nothing that could fall directly
|
||||||
|
within that overall subject. (Thus, if the Document is in part a
|
||||||
|
textbook of mathematics, a Secondary Section may not explain any
|
||||||
|
mathematics.) The relationship could be a matter of historical
|
||||||
|
connection with the subject or with related matters, or of legal,
|
||||||
|
commercial, philosophical, ethical or political position regarding
|
||||||
|
them.
|
||||||
|
|
||||||
|
The "Invariant Sections" are certain Secondary Sections whose titles
|
||||||
|
are designated, as being those of Invariant Sections, in the notice
|
||||||
|
that says that the Document is released under this License. If a
|
||||||
|
section does not fit the above definition of Secondary then it is not
|
||||||
|
allowed to be designated as Invariant. The Document may contain zero
|
||||||
|
Invariant Sections. If the Document does not identify any Invariant
|
||||||
|
Sections then there are none.
|
||||||
|
|
||||||
|
The "Cover Texts" are certain short passages of text that are listed,
|
||||||
|
as Front-Cover Texts or Back-Cover Texts, in the notice that says that
|
||||||
|
the Document is released under this License. A Front-Cover Text may
|
||||||
|
be at most 5 words, and a Back-Cover Text may be at most 25 words.
|
||||||
|
|
||||||
|
A "Transparent" copy of the Document means a machine-readable copy,
|
||||||
|
represented in a format whose specification is available to the
|
||||||
|
general public, that is suitable for revising the document
|
||||||
|
straightforwardly with generic text editors or (for images composed of
|
||||||
|
pixels) generic paint programs or (for drawings) some widely available
|
||||||
|
drawing editor, and that is suitable for input to text formatters or
|
||||||
|
for automatic translation to a variety of formats suitable for input
|
||||||
|
to text formatters. A copy made in an otherwise Transparent file
|
||||||
|
format whose markup, or absence of markup, has been arranged to thwart
|
||||||
|
or discourage subsequent modification by readers is not Transparent.
|
||||||
|
An image format is not Transparent if used for any substantial amount
|
||||||
|
of text. A copy that is not "Transparent" is called "Opaque".
|
||||||
|
|
||||||
|
Examples of suitable formats for Transparent copies include plain
|
||||||
|
ASCII without markup, Texinfo input format, LaTeX input format, SGML
|
||||||
|
or XML using a publicly available DTD, and standard-conforming simple
|
||||||
|
HTML, PostScript or PDF designed for human modification. Examples of
|
||||||
|
transparent image formats include PNG, XCF and JPG. Opaque formats
|
||||||
|
include proprietary formats that can be read and edited only by
|
||||||
|
proprietary word processors, SGML or XML for which the DTD and/or
|
||||||
|
processing tools are not generally available, and the
|
||||||
|
machine-generated HTML, PostScript or PDF produced by some word
|
||||||
|
processors for output purposes only.
|
||||||
|
|
||||||
|
The "Title Page" means, for a printed book, the title page itself,
|
||||||
|
plus such following pages as are needed to hold, legibly, the material
|
||||||
|
this License requires to appear in the title page. For works in
|
||||||
|
formats which do not have any title page as such, "Title Page" means
|
||||||
|
the text near the most prominent appearance of the work's title,
|
||||||
|
preceding the beginning of the body of the text.
|
||||||
|
|
||||||
|
A section "Entitled XYZ" means a named subunit of the Document whose
|
||||||
|
title either is precisely XYZ or contains XYZ in parentheses following
|
||||||
|
text that translates XYZ in another language. (Here XYZ stands for a
|
||||||
|
specific section name mentioned below, such as "Acknowledgements",
|
||||||
|
"Dedications", "Endorsements", or "History".) To "Preserve the Title"
|
||||||
|
of such a section when you modify the Document means that it remains a
|
||||||
|
section "Entitled XYZ" according to this definition.
|
||||||
|
|
||||||
|
The Document may include Warranty Disclaimers next to the notice which
|
||||||
|
states that this License applies to the Document. These Warranty
|
||||||
|
Disclaimers are considered to be included by reference in this
|
||||||
|
License, but only as regards disclaiming warranties: any other
|
||||||
|
implication that these Warranty Disclaimers may have is void and has
|
||||||
|
no effect on the meaning of this License.
|
||||||
|
|
||||||
|
|
||||||
|
2. VERBATIM COPYING
|
||||||
|
|
||||||
|
You may copy and distribute the Document in any medium, either
|
||||||
|
commercially or noncommercially, provided that this License, the
|
||||||
|
copyright notices, and the license notice saying this License applies
|
||||||
|
to the Document are reproduced in all copies, and that you add no other
|
||||||
|
conditions whatsoever to those of this License. You may not use
|
||||||
|
technical measures to obstruct or control the reading or further
|
||||||
|
copying of the copies you make or distribute. However, you may accept
|
||||||
|
compensation in exchange for copies. If you distribute a large enough
|
||||||
|
number of copies you must also follow the conditions in section 3.
|
||||||
|
|
||||||
|
You may also lend copies, under the same conditions stated above, and
|
||||||
|
you may publicly display copies.
|
||||||
|
|
||||||
|
|
||||||
|
3. COPYING IN QUANTITY
|
||||||
|
|
||||||
|
If you publish printed copies (or copies in media that commonly have
|
||||||
|
printed covers) of the Document, numbering more than 100, and the
|
||||||
|
Document's license notice requires Cover Texts, you must enclose the
|
||||||
|
copies in covers that carry, clearly and legibly, all these Cover
|
||||||
|
Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
|
||||||
|
the back cover. Both covers must also clearly and legibly identify
|
||||||
|
you as the publisher of these copies. The front cover must present
|
||||||
|
the full title with all words of the title equally prominent and
|
||||||
|
visible. You may add other material on the covers in addition.
|
||||||
|
Copying with changes limited to the covers, as long as they preserve
|
||||||
|
the title of the Document and satisfy these conditions, can be treated
|
||||||
|
as verbatim copying in other respects.
|
||||||
|
|
||||||
|
If the required texts for either cover are too voluminous to fit
|
||||||
|
legibly, you should put the first ones listed (as many as fit
|
||||||
|
reasonably) on the actual cover, and continue the rest onto adjacent
|
||||||
|
pages.
|
||||||
|
|
||||||
|
If you publish or distribute Opaque copies of the Document numbering
|
||||||
|
more than 100, you must either include a machine-readable Transparent
|
||||||
|
copy along with each Opaque copy, or state in or with each Opaque copy
|
||||||
|
a computer-network location from which the general network-using
|
||||||
|
public has access to download using public-standard network protocols
|
||||||
|
a complete Transparent copy of the Document, free of added material.
|
||||||
|
If you use the latter option, you must take reasonably prudent steps,
|
||||||
|
when you begin distribution of Opaque copies in quantity, to ensure
|
||||||
|
that this Transparent copy will remain thus accessible at the stated
|
||||||
|
location until at least one year after the last time you distribute an
|
||||||
|
Opaque copy (directly or through your agents or retailers) of that
|
||||||
|
edition to the public.
|
||||||
|
|
||||||
|
It is requested, but not required, that you contact the authors of the
|
||||||
|
Document well before redistributing any large number of copies, to give
|
||||||
|
them a chance to provide you with an updated version of the Document.
|
||||||
|
|
||||||
|
|
||||||
|
4. MODIFICATIONS
|
||||||
|
|
||||||
|
You may copy and distribute a Modified Version of the Document under
|
||||||
|
the conditions of sections 2 and 3 above, provided that you release
|
||||||
|
the Modified Version under precisely this License, with the Modified
|
||||||
|
Version filling the role of the Document, thus licensing distribution
|
||||||
|
and modification of the Modified Version to whoever possesses a copy
|
||||||
|
of it. In addition, you must do these things in the Modified Version:
|
||||||
|
|
||||||
|
A. Use in the Title Page (and on the covers, if any) a title distinct
|
||||||
|
from that of the Document, and from those of previous versions
|
||||||
|
(which should, if there were any, be listed in the History section
|
||||||
|
of the Document). You may use the same title as a previous version
|
||||||
|
if the original publisher of that version gives permission.
|
||||||
|
B. List on the Title Page, as authors, one or more persons or entities
|
||||||
|
responsible for authorship of the modifications in the Modified
|
||||||
|
Version, together with at least five of the principal authors of the
|
||||||
|
Document (all of its principal authors, if it has fewer than five),
|
||||||
|
unless they release you from this requirement.
|
||||||
|
C. State on the Title page the name of the publisher of the
|
||||||
|
Modified Version, as the publisher.
|
||||||
|
D. Preserve all the copyright notices of the Document.
|
||||||
|
E. Add an appropriate copyright notice for your modifications
|
||||||
|
adjacent to the other copyright notices.
|
||||||
|
F. Include, immediately after the copyright notices, a license notice
|
||||||
|
giving the public permission to use the Modified Version under the
|
||||||
|
terms of this License, in the form shown in the Addendum below.
|
||||||
|
G. Preserve in that license notice the full lists of Invariant Sections
|
||||||
|
and required Cover Texts given in the Document's license notice.
|
||||||
|
H. Include an unaltered copy of this License.
|
||||||
|
I. Preserve the section Entitled "History", Preserve its Title, and add
|
||||||
|
to it an item stating at least the title, year, new authors, and
|
||||||
|
publisher of the Modified Version as given on the Title Page. If
|
||||||
|
there is no section Entitled "History" in the Document, create one
|
||||||
|
stating the title, year, authors, and publisher of the Document as
|
||||||
|
given on its Title Page, then add an item describing the Modified
|
||||||
|
Version as stated in the previous sentence.
|
||||||
|
J. Preserve the network location, if any, given in the Document for
|
||||||
|
public access to a Transparent copy of the Document, and likewise
|
||||||
|
the network locations given in the Document for previous versions
|
||||||
|
it was based on. These may be placed in the "History" section.
|
||||||
|
You may omit a network location for a work that was published at
|
||||||
|
least four years before the Document itself, or if the original
|
||||||
|
publisher of the version it refers to gives permission.
|
||||||
|
K. For any section Entitled "Acknowledgements" or "Dedications",
|
||||||
|
Preserve the Title of the section, and preserve in the section all
|
||||||
|
the substance and tone of each of the contributor acknowledgements
|
||||||
|
and/or dedications given therein.
|
||||||
|
L. Preserve all the Invariant Sections of the Document,
|
||||||
|
unaltered in their text and in their titles. Section numbers
|
||||||
|
or the equivalent are not considered part of the section titles.
|
||||||
|
M. Delete any section Entitled "Endorsements". Such a section
|
||||||
|
may not be included in the Modified Version.
|
||||||
|
N. Do not retitle any existing section to be Entitled "Endorsements"
|
||||||
|
or to conflict in title with any Invariant Section.
|
||||||
|
O. Preserve any Warranty Disclaimers.
|
||||||
|
|
||||||
|
If the Modified Version includes new front-matter sections or
|
||||||
|
appendices that qualify as Secondary Sections and contain no material
|
||||||
|
copied from the Document, you may at your option designate some or all
|
||||||
|
of these sections as invariant. To do this, add their titles to the
|
||||||
|
list of Invariant Sections in the Modified Version's license notice.
|
||||||
|
These titles must be distinct from any other section titles.
|
||||||
|
|
||||||
|
You may add a section Entitled "Endorsements", provided it contains
|
||||||
|
nothing but endorsements of your Modified Version by various
|
||||||
|
parties--for example, statements of peer review or that the text has
|
||||||
|
been approved by an organization as the authoritative definition of a
|
||||||
|
standard.
|
||||||
|
|
||||||
|
You may add a passage of up to five words as a Front-Cover Text, and a
|
||||||
|
passage of up to 25 words as a Back-Cover Text, to the end of the list
|
||||||
|
of Cover Texts in the Modified Version. Only one passage of
|
||||||
|
Front-Cover Text and one of Back-Cover Text may be added by (or
|
||||||
|
through arrangements made by) any one entity. If the Document already
|
||||||
|
includes a cover text for the same cover, previously added by you or
|
||||||
|
by arrangement made by the same entity you are acting on behalf of,
|
||||||
|
you may not add another; but you may replace the old one, on explicit
|
||||||
|
permission from the previous publisher that added the old one.
|
||||||
|
|
||||||
|
The author(s) and publisher(s) of the Document do not by this License
|
||||||
|
give permission to use their names for publicity for or to assert or
|
||||||
|
imply endorsement of any Modified Version.
|
||||||
|
|
||||||
|
|
||||||
|
5. COMBINING DOCUMENTS
|
||||||
|
|
||||||
|
You may combine the Document with other documents released under this
|
||||||
|
License, under the terms defined in section 4 above for modified
|
||||||
|
versions, provided that you include in the combination all of the
|
||||||
|
Invariant Sections of all of the original documents, unmodified, and
|
||||||
|
list them all as Invariant Sections of your combined work in its
|
||||||
|
license notice, and that you preserve all their Warranty Disclaimers.
|
||||||
|
|
||||||
|
The combined work need only contain one copy of this License, and
|
||||||
|
multiple identical Invariant Sections may be replaced with a single
|
||||||
|
copy. If there are multiple Invariant Sections with the same name but
|
||||||
|
different contents, make the title of each such section unique by
|
||||||
|
adding at the end of it, in parentheses, the name of the original
|
||||||
|
author or publisher of that section if known, or else a unique number.
|
||||||
|
Make the same adjustment to the section titles in the list of
|
||||||
|
Invariant Sections in the license notice of the combined work.
|
||||||
|
|
||||||
|
In the combination, you must combine any sections Entitled "History"
|
||||||
|
in the various original documents, forming one section Entitled
|
||||||
|
"History"; likewise combine any sections Entitled "Acknowledgements",
|
||||||
|
and any sections Entitled "Dedications". You must delete all sections
|
||||||
|
Entitled "Endorsements".
|
||||||
|
|
||||||
|
|
||||||
|
6. COLLECTIONS OF DOCUMENTS
|
||||||
|
|
||||||
|
You may make a collection consisting of the Document and other documents
|
||||||
|
released under this License, and replace the individual copies of this
|
||||||
|
License in the various documents with a single copy that is included in
|
||||||
|
the collection, provided that you follow the rules of this License for
|
||||||
|
verbatim copying of each of the documents in all other respects.
|
||||||
|
|
||||||
|
You may extract a single document from such a collection, and distribute
|
||||||
|
it individually under this License, provided you insert a copy of this
|
||||||
|
License into the extracted document, and follow this License in all
|
||||||
|
other respects regarding verbatim copying of that document.
|
||||||
|
|
||||||
|
|
||||||
|
7. AGGREGATION WITH INDEPENDENT WORKS
|
||||||
|
|
||||||
|
A compilation of the Document or its derivatives with other separate
|
||||||
|
and independent documents or works, in or on a volume of a storage or
|
||||||
|
distribution medium, is called an "aggregate" if the copyright
|
||||||
|
resulting from the compilation is not used to limit the legal rights
|
||||||
|
of the compilation's users beyond what the individual works permit.
|
||||||
|
When the Document is included in an aggregate, this License does not
|
||||||
|
apply to the other works in the aggregate which are not themselves
|
||||||
|
derivative works of the Document.
|
||||||
|
|
||||||
|
If the Cover Text requirement of section 3 is applicable to these
|
||||||
|
copies of the Document, then if the Document is less than one half of
|
||||||
|
the entire aggregate, the Document's Cover Texts may be placed on
|
||||||
|
covers that bracket the Document within the aggregate, or the
|
||||||
|
electronic equivalent of covers if the Document is in electronic form.
|
||||||
|
Otherwise they must appear on printed covers that bracket the whole
|
||||||
|
aggregate.
|
||||||
|
|
||||||
|
|
||||||
|
8. TRANSLATION
|
||||||
|
|
||||||
|
Translation is considered a kind of modification, so you may
|
||||||
|
distribute translations of the Document under the terms of section 4.
|
||||||
|
Replacing Invariant Sections with translations requires special
|
||||||
|
permission from their copyright holders, but you may include
|
||||||
|
translations of some or all Invariant Sections in addition to the
|
||||||
|
original versions of these Invariant Sections. You may include a
|
||||||
|
translation of this License, and all the license notices in the
|
||||||
|
Document, and any Warranty Disclaimers, provided that you also include
|
||||||
|
the original English version of this License and the original versions
|
||||||
|
of those notices and disclaimers. In case of a disagreement between
|
||||||
|
the translation and the original version of this License or a notice
|
||||||
|
or disclaimer, the original version will prevail.
|
||||||
|
|
||||||
|
If a section in the Document is Entitled "Acknowledgements",
|
||||||
|
"Dedications", or "History", the requirement (section 4) to Preserve
|
||||||
|
its Title (section 1) will typically require changing the actual
|
||||||
|
title.
|
||||||
|
|
||||||
|
|
||||||
|
9. TERMINATION
|
||||||
|
|
||||||
|
You may not copy, modify, sublicense, or distribute the Document except
|
||||||
|
as expressly provided for under this License. Any other attempt to
|
||||||
|
copy, modify, sublicense or distribute the Document 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.
|
||||||
|
|
||||||
|
|
||||||
|
10. FUTURE REVISIONS OF THIS LICENSE
|
||||||
|
|
||||||
|
The Free Software Foundation may publish new, revised versions
|
||||||
|
of the GNU Free Documentation 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. See
|
||||||
|
http://www.gnu.org/copyleft/.
|
||||||
|
|
||||||
|
Each version of the License is given a distinguishing version number.
|
||||||
|
If the Document specifies that a particular numbered version of this
|
||||||
|
License "or any later version" applies to it, you have the option of
|
||||||
|
following the terms and conditions either of that specified version or
|
||||||
|
of any later version that has been published (not as a draft) by the
|
||||||
|
Free Software Foundation. If the Document does not specify a version
|
||||||
|
number of this License, you may choose any version ever published (not
|
||||||
|
as a draft) by the Free Software Foundation.
|
||||||
|
|
||||||
|
|
||||||
|
ADDENDUM: How to use this License for your documents
|
||||||
|
|
||||||
|
To use this License in a document you have written, include a copy of
|
||||||
|
the License in the document and put the following copyright and
|
||||||
|
license notices just after the title page:
|
||||||
|
|
||||||
|
Copyright (c) YEAR YOUR NAME.
|
||||||
|
Permission is granted to copy, distribute and/or modify this document
|
||||||
|
under the terms of the GNU Free Documentation License, Version 1.2
|
||||||
|
or any later version published by the Free Software Foundation;
|
||||||
|
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
||||||
|
A copy of the license is included in the section entitled "GNU
|
||||||
|
Free Documentation License".
|
||||||
|
|
||||||
|
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
|
||||||
|
replace the "with...Texts." line with this:
|
||||||
|
|
||||||
|
with the Invariant Sections being LIST THEIR TITLES, with the
|
||||||
|
Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
|
||||||
|
|
||||||
|
If you have Invariant Sections without Cover Texts, or some other
|
||||||
|
combination of the three, merge those two alternatives to suit the
|
||||||
|
situation.
|
||||||
|
|
||||||
|
If your document contains nontrivial examples of program code, we
|
||||||
|
recommend releasing these examples in parallel under your choice of
|
||||||
|
free software license, such as the GNU General Public License,
|
||||||
|
to permit their use in free software.
|
6
kcachegrind/ChangeLog
Normal file
6
kcachegrind/ChangeLog
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
2011/11/14
|
||||||
|
* KCachegrind: use callgrind_control to request a dump.
|
||||||
|
The old way will not work with Callgrind in VG > 3.7
|
||||||
|
|
||||||
|
2011/9/23
|
||||||
|
* Compiles without Qt3 support
|
5
kcachegrind/INSTALL
Normal file
5
kcachegrind/INSTALL
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
For build instructions see README.
|
||||||
|
|
||||||
|
More details for KDE build:
|
||||||
|
http://techbase.kde.org/Getting_Started/Build/KDE4
|
||||||
|
|
23
kcachegrind/KnownBugs
Normal file
23
kcachegrind/KnownBugs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
Known Bugs
|
||||||
|
|
||||||
|
* Cost values do not update after the formula of a derived event type
|
||||||
|
was changed. One has to change to another event type and back (9/2011)
|
||||||
|
|
||||||
|
Reason: For derived event types, class ProfileCostArray caches the cost,
|
||||||
|
calculated from the formula, as this is used multiple times. The cache
|
||||||
|
is only invalidated when the cost of another event type is requested.
|
||||||
|
|
||||||
|
Possible fix: Invalidate the cache of every cost item after a formula
|
||||||
|
change. Tricky and takes time. Better allocate a new event type and
|
||||||
|
remove old?
|
||||||
|
|
||||||
|
* KCachegrind allows two event types to be shown. If they are set to the
|
||||||
|
same, and this event type is removed, KCachegrind crashes.
|
||||||
|
|
||||||
|
Reason: By design, only one of the event types can be changed at a time.
|
||||||
|
Still, on a change, the cost for the other event type is updated. As
|
||||||
|
the event type is already deleted, the crash happens.
|
||||||
|
|
||||||
|
Possible fix: delete event type after selecting another event type before.
|
||||||
|
Tricky, as change actions are delayed by a timer.
|
||||||
|
Another fix: do not allow the two event types to be the same.
|
16
kcachegrind/Mainpage.dox
Normal file
16
kcachegrind/Mainpage.dox
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/** @mainpage KCachegrind
|
||||||
|
*
|
||||||
|
* KCachegrind is a browser for data produced by profiling tools.
|
||||||
|
*
|
||||||
|
* KCachegrind features various visualization views for a selected function, namely
|
||||||
|
* - a call-graph view, which shows a section of the call graph around the
|
||||||
|
* selected function,
|
||||||
|
* - a tree-map view, which allows nested-call relations to be visualized,
|
||||||
|
* together with inclusive cost metric for fast visual detection of
|
||||||
|
* problematic functions,
|
||||||
|
* - source code and disassembler annotation views, allowing to see details of
|
||||||
|
* cost related to source lines and assembler instructions.
|
||||||
|
*
|
||||||
|
* KCachegrind is part of the KDE SDK package, and it is released at the same time as
|
||||||
|
* the rest of KDE SDK and KDE.
|
||||||
|
*/
|
6
kcachegrind/Messages.sh
Normal file
6
kcachegrind/Messages.sh
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#! /bin/sh
|
||||||
|
(cd kcachegrind;$PREPARETIPS) > tips.cpp
|
||||||
|
$EXTRACTRC `find kcachegrind -name \*.ui -o -name \*.rc -o -name \*.kcfg` >> rc.cpp
|
||||||
|
$XGETTEXT rc.cpp `find kcachegrind -name '*.cpp'` tips.cpp -o $podir/kcachegrind.pot
|
||||||
|
$XGETTEXT_QT `find libcore -name '*.cpp'` `find libviews -name '*.cpp'` -o $podir/kcachegrind_qt.pot
|
||||||
|
rm -f tips.cpp rc.cpp
|
0
kcachegrind/NEWS
Normal file
0
kcachegrind/NEWS
Normal file
108
kcachegrind/README
Normal file
108
kcachegrind/README
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
KCachegrind / QCachegrind
|
||||||
|
-========================
|
||||||
|
|
||||||
|
{K,Q}Cachegrind is a KDE/Qt GUI to visualize profiling data.
|
||||||
|
It's mainly used as visualization frontend for data measured
|
||||||
|
by Cachegrind/Callgrind tools from the Valgrind package, but
|
||||||
|
there are converters for other measurement tools available.
|
||||||
|
|
||||||
|
Features
|
||||||
|
|
||||||
|
* direct support for profiles generated by Cachegrind/Callgrind
|
||||||
|
* support for arbitrary event types and derived event types
|
||||||
|
* sorted function list, with grouping according to ELF object/source
|
||||||
|
file/symbol namespace (such as C++ classes)
|
||||||
|
* correct handling of recursive cycles (similar to GProf)
|
||||||
|
* various visualization views for a selected function, such as
|
||||||
|
- treemap in caller/callee direction
|
||||||
|
- call graph around function
|
||||||
|
- source & assembly annotation
|
||||||
|
|
||||||
|
|
||||||
|
Hmm. What is stuff good for?
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Any work in improving the performance of a program should be
|
||||||
|
started with measuring the performance characteristics of the
|
||||||
|
optimized binary, using representative input data. This process
|
||||||
|
is called "Profiling". Any other way for performance optimization
|
||||||
|
usually just wastes developer time.
|
||||||
|
Profile measurements show whether optimization is needed at all,
|
||||||
|
and what improvement can be expected. Further, it pinpoint at
|
||||||
|
functions and source lines where most time is spent, i.e. where an
|
||||||
|
improvement has most influence on allover runtime.
|
||||||
|
|
||||||
|
{K,Q}Cachegrind visualizes profile measurement data. Example of an
|
||||||
|
easy to use profile measurement tool (no source modifications and
|
||||||
|
recompilation is required, as well as no root access) are the
|
||||||
|
cache simulators Cachegrind and Callgrind from the Valgrind toolset.
|
||||||
|
While {K,Q}Cachegrind directly supports the formats of these
|
||||||
|
profiling tools, converters are available to allow to import data
|
||||||
|
from other profiling tools, too.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Compilation and Installation
|
||||||
|
-===========================
|
||||||
|
|
||||||
|
|
||||||
|
QCachegrind
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
* Qt5.x (x >=0) or Qt4.y (y >=5) (earlier versions not tested)
|
||||||
|
* Any platform supported by Qt (Linux, Windows, Mac OS X, ...)
|
||||||
|
|
||||||
|
Compilation (from base directory):
|
||||||
|
|
||||||
|
qmake; make
|
||||||
|
|
||||||
|
To not build in the source directories, do:
|
||||||
|
|
||||||
|
mkdir build; cd build; qmake ../qcg.pro; make
|
||||||
|
|
||||||
|
The build includes the command line tool "cgview".
|
||||||
|
|
||||||
|
Copy the resulting "qcachegrind" binary from the build directory into
|
||||||
|
your $PATH. For better desktop integration, it should be enough to
|
||||||
|
copy the .desktop file and icons into standard places, such as:
|
||||||
|
|
||||||
|
sudo install -m 755 qcachegrind/qcachegrind /usr/local/bin
|
||||||
|
sudo install -m 644 qcachegrind/qcachegrind.desktop \
|
||||||
|
/usr/local/share/applications/
|
||||||
|
sudo install -m 644 kcachegrind/hi32-app-kcachegrind.png \
|
||||||
|
/usr/local/share/icons/hicolor/32x32/apps/kcachegrind.png
|
||||||
|
sudo install -m 644 kcachegrind/hi48-app-kcachegrind.png \
|
||||||
|
/usr/local/share/icons/hicolor/48x48/apps/kcachegrind.png
|
||||||
|
|
||||||
|
|
||||||
|
KCachegrind
|
||||||
|
------------
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
* KDE 4.3 or higher: kdelibs development packages (libs & headers)
|
||||||
|
* CMake
|
||||||
|
|
||||||
|
Commands (from base directory):
|
||||||
|
|
||||||
|
cmake .; make; make install
|
||||||
|
|
||||||
|
To not build in the source directories, do:
|
||||||
|
|
||||||
|
mkdir build; cd build; cmake ..; make; make install
|
||||||
|
|
||||||
|
The build also compiles the command line tool "cgview" and "qcachegrind",
|
||||||
|
the Qt-only version of KCachegrind. However, these are not installed.
|
||||||
|
If you want to also install qcachegrind, see instructions above.
|
||||||
|
|
||||||
|
|
||||||
|
Usage & Help
|
||||||
|
-===========
|
||||||
|
|
||||||
|
{K,Q}Cachegrind has detailed "What's this?" help for
|
||||||
|
each GUI part. For further help, see quick start pages
|
||||||
|
on kcachegrind.sf.net
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Josef Weidendorfer
|
94
kcachegrind/TODO
Normal file
94
kcachegrind/TODO
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
TODO/Wishlist Items
|
||||||
|
===================
|
||||||
|
|
||||||
|
Support for KCachegrind in Calltree
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
<<<<<<< TODO
|
||||||
|
BUGS:
|
||||||
|
- discarded separated by objname
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> 1.1.2.1
|
||||||
|
WISHS:
|
||||||
|
- store more details of calltree
|
||||||
|
- for every function call: executed from shared lib
|
||||||
|
(Not needed, if function names are unique in whole app)
|
||||||
|
- adaptive call chain context (Really needed ? MUCH Data!)
|
||||||
|
- dump at
|
||||||
|
- breakpoints
|
||||||
|
- watchpoints (with data tracing!)
|
||||||
|
- every xxx BBs (DONE)
|
||||||
|
- dump around
|
||||||
|
- function invocation
|
||||||
|
- KAction event
|
||||||
|
- DCOP event
|
||||||
|
|
||||||
|
- data accesses from (instr address/count)
|
||||||
|
stack: -> (function, stackframe-offset)
|
||||||
|
dynamic: -> (mem region start, [type], offset)
|
||||||
|
type can be get when a constructor is called for region
|
||||||
|
static: -> (mem region start, type, offset)
|
||||||
|
|
||||||
|
* Generate full instr/data access trace for offline analysis.
|
||||||
|
|
||||||
|
* Appending mode
|
||||||
|
|
||||||
|
|
||||||
|
KCachegrind
|
||||||
|
-----------
|
||||||
|
|
||||||
|
All cost Lists:
|
||||||
|
* Show up to a number of items, not down to a threadshold.
|
||||||
|
If more, add a "..." with number of items not shown, and context option
|
||||||
|
to show more
|
||||||
|
* "Copy from Top" converts lists into ASCII, puts into clipboard
|
||||||
|
|
||||||
|
|
||||||
|
Configuration:
|
||||||
|
Source dirs per ELF object
|
||||||
|
|
||||||
|
Layout:
|
||||||
|
* 1/2/3/4 vertical/horizontal FunctionInfos
|
||||||
|
with Shift/Wraparound selection mode
|
||||||
|
* Inside each FunctionInfo different Layouts
|
||||||
|
- tabbed layout
|
||||||
|
- top: info, bottom left: calls/coverage, bottom right: graph/source
|
||||||
|
* Long/short info tab
|
||||||
|
|
||||||
|
General:
|
||||||
|
* Selected Item can be a object/file/class/function/line
|
||||||
|
* Configuration Dlg
|
||||||
|
- Local config (?)
|
||||||
|
- Cost Types
|
||||||
|
- function colors
|
||||||
|
- Try to reload source after config.
|
||||||
|
* Session Management
|
||||||
|
|
||||||
|
Assembler view:
|
||||||
|
* Option for context lines
|
||||||
|
|
||||||
|
Source view:
|
||||||
|
* Option for context lines
|
||||||
|
* Implicit jumps (green)
|
||||||
|
|
||||||
|
Callgraph:
|
||||||
|
* Fix Arrows for back-arcs
|
||||||
|
* Less "Jumps" for minimap
|
||||||
|
* Correct Keyboard navigation (how?)
|
||||||
|
|
||||||
|
Types:
|
||||||
|
* Ratios
|
||||||
|
* Automatic subtypes
|
||||||
|
|
||||||
|
WISHS:
|
||||||
|
* Support for Data tracing
|
||||||
|
Which variables are touched how often from which function?
|
||||||
|
- Some graphical visualisation...
|
||||||
|
|
||||||
|
* GCC -pg (gmon.out) as Profiling Backend
|
||||||
|
* Demangler (use c++filt)
|
||||||
|
* Calculation of call weights (if not given)
|
||||||
|
* OProfile, DynaProf
|
||||||
|
|
||||||
|
|
7
kcachegrind/cgview/CMakeLists.txt
Normal file
7
kcachegrind/cgview/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
include_directories(../libcore)
|
||||||
|
add_executable(cgview main.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(cgview core ${QT_QTCORE_LIBRARY})
|
||||||
|
|
||||||
|
# do not install example code...
|
||||||
|
# install(TARGETS cgview ${INSTALL_TARGETS_DEFAULT_ARGS} )
|
9
kcachegrind/cgview/README
Normal file
9
kcachegrind/cgview/README
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Currently, this directory just contains _example_ code for using
|
||||||
|
KCachegrind's libcore for command line tools. It is not expected
|
||||||
|
to be packaged with KCachegrind (therefore, nothing is installed).
|
||||||
|
|
||||||
|
Eventually, it will grow into a convertion/merge/filter tool for
|
||||||
|
profile data, and a replacement for the callgrind_annotate
|
||||||
|
script from the Valgrind package (which is broken as it does not
|
||||||
|
handle profile data with instruction granularity, as well as
|
||||||
|
cycle detection).
|
16
kcachegrind/cgview/cgview.pro
Normal file
16
kcachegrind/cgview/cgview.pro
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
TEMPLATE = app
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
include(../libcore/libcore.pri)
|
||||||
|
|
||||||
|
# This generate *.moc files from NHEADERS, which get included from *.cpp
|
||||||
|
new_moc.CONFIG = no_link moc_verify
|
||||||
|
new_moc.output = ${QMAKE_FILE_BASE}.moc
|
||||||
|
new_moc.commands = $$moc_header.commands
|
||||||
|
new_moc.input = NHEADERS
|
||||||
|
QMAKE_EXTRA_COMPILERS = new_moc
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
||||||
|
|
||||||
|
# makes headers visible in qt-creator
|
||||||
|
HEADERS += $$NHEADERS
|
193
kcachegrind/cgview/main.cpp
Normal file
193
kcachegrind/cgview/main.cpp
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2008 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
#include "tracedata.h"
|
||||||
|
#include "loader.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "globalconfig.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Just a simple command line tool using libcore
|
||||||
|
*/
|
||||||
|
|
||||||
|
void showHelp(QTextStream& out, bool fullHelp = true)
|
||||||
|
{
|
||||||
|
out << "Show profiles from callgrind files. (C) 2010 J. Weidendorfer\n";
|
||||||
|
|
||||||
|
if (!fullHelp)
|
||||||
|
out << "Type 'cgview -h' for help." << endl;
|
||||||
|
else
|
||||||
|
out << "Usage: cgview [options] <file> ...\n\n"
|
||||||
|
"Options:\n"
|
||||||
|
" -h Show this help text\n"
|
||||||
|
" -e Sort list according to exclusive cost\n"
|
||||||
|
" -s <ev> Sort and show counters for event <ev>\n"
|
||||||
|
" -c Sort by call count\n"
|
||||||
|
" -b Show butterfly (callers and callees)\n"
|
||||||
|
" -n Do not detect recursive cycles" << endl;
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
QTextStream out(stdout);
|
||||||
|
|
||||||
|
Loader::initLoaders();
|
||||||
|
ConfigStorage::setStorage(new ConfigStorage);
|
||||||
|
GlobalConfig::config()->addDefaultTypes();
|
||||||
|
|
||||||
|
QStringList list = app.arguments();
|
||||||
|
list.pop_front();
|
||||||
|
if (list.isEmpty()) showHelp(out, false);
|
||||||
|
|
||||||
|
bool sortByExcl = false;
|
||||||
|
bool sortByCount = false;
|
||||||
|
bool showCalls = false;
|
||||||
|
QString showEvent;
|
||||||
|
QStringList files;
|
||||||
|
|
||||||
|
for(int arg = 0; arg<list.count(); arg++) {
|
||||||
|
if (list[arg] == "-h") showHelp(out);
|
||||||
|
else if (list[arg] == "-e") sortByExcl = true;
|
||||||
|
else if (list[arg] == "-n") GlobalConfig::setShowCycles(false);
|
||||||
|
else if (list[arg] == "-b") showCalls = true;
|
||||||
|
else if (list[arg] == "-c") sortByCount = true;
|
||||||
|
else if (list[arg] == "-s") showEvent = list[++arg];
|
||||||
|
else
|
||||||
|
files << list[arg];
|
||||||
|
}
|
||||||
|
TraceData* d = new TraceData(new Logger);
|
||||||
|
d->load(files);
|
||||||
|
|
||||||
|
EventTypeSet* m = d->eventTypes();
|
||||||
|
if (m->realCount() == 0) {
|
||||||
|
out << "Error: No event types found." << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "\nTotals for event types:\n";
|
||||||
|
|
||||||
|
QString p;
|
||||||
|
EventType* et;
|
||||||
|
for (int i=0;i<m->realCount();i++) {
|
||||||
|
et = m->realType(i);
|
||||||
|
out.setFieldWidth(14);
|
||||||
|
out.setFieldAlignment(QTextStream::AlignRight);
|
||||||
|
out << d->subCost(et).pretty();
|
||||||
|
out.setFieldWidth(0);
|
||||||
|
out << " " << et->longName() << " (" << et->name() << ")\n";
|
||||||
|
}
|
||||||
|
for (int i=0;i<m->derivedCount();i++) {
|
||||||
|
et = m->derivedType(i);
|
||||||
|
out.setFieldWidth(14);
|
||||||
|
out.setFieldAlignment(QTextStream::AlignRight);
|
||||||
|
out << d->subCost(et).pretty();
|
||||||
|
out.setFieldWidth(0);
|
||||||
|
out << " " << et->longName() <<
|
||||||
|
" (" << et->name() << " = " << et->formula() << ")\n";
|
||||||
|
}
|
||||||
|
out << endl;
|
||||||
|
|
||||||
|
if (showEvent.isEmpty())
|
||||||
|
et = m->realType(0);
|
||||||
|
else {
|
||||||
|
et = m->type(showEvent);
|
||||||
|
if (!et) {
|
||||||
|
out << "Error: event '" << showEvent << "' not found." << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Q_ASSERT( et!=0 );
|
||||||
|
out << "Sorted by: " << (sortByExcl ? "Exclusive ":"Inclusive ")
|
||||||
|
<< et->longName() << " (" << et->name() << ")" << endl;
|
||||||
|
|
||||||
|
QList<TraceFunction*> flist;
|
||||||
|
HighestCostList hc;
|
||||||
|
hc.clear(50);
|
||||||
|
TraceFunctionMap::Iterator it;
|
||||||
|
for ( it = d->functionMap().begin(); it != d->functionMap().end(); ++it )
|
||||||
|
flist.append(&(*it));
|
||||||
|
|
||||||
|
TraceFunction *f;
|
||||||
|
foreach(f, d->functionCycles())
|
||||||
|
flist.append(f);
|
||||||
|
|
||||||
|
foreach(f, flist) {
|
||||||
|
if (sortByCount)
|
||||||
|
hc.addCost(f, f->calledCount());
|
||||||
|
else if (sortByExcl)
|
||||||
|
hc.addCost(f, f->subCost(et));
|
||||||
|
else
|
||||||
|
hc.addCost(f, f->inclusive()->subCost(et));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
out << "\n Inclusive Exclusive Called Function name (DSO)\n";
|
||||||
|
out << " ==================================================================\n";
|
||||||
|
|
||||||
|
out.setFieldAlignment(QTextStream::AlignRight);
|
||||||
|
for(int i=0; i<hc.realCount(); i++) {
|
||||||
|
f = (TraceFunction*)hc[i];
|
||||||
|
|
||||||
|
if (showCalls) {
|
||||||
|
if (i>0) out << endl;
|
||||||
|
foreach(TraceCall* c, f->callers()) {
|
||||||
|
out << " ";
|
||||||
|
out.setFieldWidth(14);
|
||||||
|
out << c->subCost(et).pretty();
|
||||||
|
out.setFieldWidth(0);
|
||||||
|
out << " ";
|
||||||
|
out.setFieldWidth(13);
|
||||||
|
out << c->prettyCallCount();
|
||||||
|
out.setFieldWidth(0);
|
||||||
|
out << " < " << c->caller()->prettyName() << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.setFieldWidth(14);
|
||||||
|
out << f->inclusive()->subCost(et).pretty();
|
||||||
|
out << f->subCost(et).pretty();
|
||||||
|
out.setFieldWidth(13);
|
||||||
|
out << f->prettyCalledCount();
|
||||||
|
out.setFieldWidth(0);
|
||||||
|
out << " " << f->name() << " (" << f->object()->name() << ")" << endl;
|
||||||
|
|
||||||
|
if (showCalls) {
|
||||||
|
foreach(TraceCall* c, f->callings()) {
|
||||||
|
out << " ";
|
||||||
|
out.setFieldWidth(14);
|
||||||
|
out << c->subCost(et).pretty();
|
||||||
|
out.setFieldWidth(0);
|
||||||
|
out << " ";
|
||||||
|
out.setFieldWidth(13);
|
||||||
|
out << c->prettyCallCount();
|
||||||
|
out.setFieldWidth(0);
|
||||||
|
out << " > " << c->called()->prettyName() << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
11
kcachegrind/converters/CMakeLists.txt
Normal file
11
kcachegrind/converters/CMakeLists.txt
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/hotshot2calltree.cmake
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/hotshot2calltree
|
||||||
|
)
|
||||||
|
macro_additional_clean_files(
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/hotshot2calltree
|
||||||
|
)
|
||||||
|
|
||||||
|
install( PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/hotshot2calltree
|
||||||
|
op2calltree pprof2calltree dprof2calltree memprof2calltree
|
||||||
|
DESTINATION ${BIN_INSTALL_DIR} )
|
24
kcachegrind/converters/README
Normal file
24
kcachegrind/converters/README
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
This directory contains some scripts to convert output of different
|
||||||
|
profiling tools into the format which can be loaded by KCachegrind.
|
||||||
|
See the comment at start of every script for details.
|
||||||
|
|
||||||
|
In the long run, these should be replaced by import filters in
|
||||||
|
KCachegrind directly, but I can't promise anything. Partly, this
|
||||||
|
is because some scripts are provided as contribution from others.
|
||||||
|
|
||||||
|
hotshot2calltree Converter from Python Hotshot Profiler.
|
||||||
|
op2calltree Converter from OProfile sampling data.
|
||||||
|
dprof2calltree Converter from PERL::DProf Profiler.
|
||||||
|
pprof2calltree Converter from APD PHP Profiler.
|
||||||
|
|
||||||
|
Thanks go to
|
||||||
|
* George Schlossnagle <george@omniti.com> for
|
||||||
|
dprof2calltree and pprof2calltree,
|
||||||
|
* Jörg Beyer <job@webde-ag.de> for
|
||||||
|
hotshot2calltree
|
||||||
|
|
||||||
|
If you want to write a converter, have a look at the calltree format
|
||||||
|
description on the web site (kcachegrind.sf.net).
|
||||||
|
|
||||||
|
Josef
|
||||||
|
|
199
kcachegrind/converters/dprof2calltree
Normal file
199
kcachegrind/converters/dprof2calltree
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# - Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# - Redistributions in binary form must reproduce the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# - All advertising materials mentioning features or use of this software
|
||||||
|
# must display the following acknowledgement: This product includes software
|
||||||
|
# developed by OmniTI Computer Consulting.
|
||||||
|
#
|
||||||
|
# - Neither name of the company nor the names of its contributors may be
|
||||||
|
# used to endorse or promote products derived from this software without
|
||||||
|
# specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS `AS IS'' AND ANY
|
||||||
|
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2004 OmniTI Computer Consulting
|
||||||
|
# All rights reserved
|
||||||
|
# The following code was written by George Schlossnagle <george@omniti.com>
|
||||||
|
# and is provided completely free and without any warranty.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# This script is designed to convert the tmon.out output emitted
|
||||||
|
# from Perl's Devel::DProf profiling package. To use this:
|
||||||
|
#
|
||||||
|
# 1) Run your perl script as
|
||||||
|
# > perl -d:DProf yoursript.pl
|
||||||
|
# This will create a file called tmon.out. If you want to
|
||||||
|
# inspect it on the command line, look at the man page
|
||||||
|
# for dprofp for details.
|
||||||
|
#
|
||||||
|
# 2) Run
|
||||||
|
# > dprof2calltree -f tmon.out
|
||||||
|
# or
|
||||||
|
# > dprof2calltree -f tmon.out -o cachegrind.out.foo
|
||||||
|
#
|
||||||
|
# This creates a cachegrind-style file called cachgrind.out.tmon.out or
|
||||||
|
# cachegrind.out.foo, respecitvely.
|
||||||
|
#
|
||||||
|
# 3) Run kcachegrind cachegrind.out.foo
|
||||||
|
#
|
||||||
|
# 4) Enjoy!
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use Config;
|
||||||
|
use Getopt::Std;
|
||||||
|
use IO::File;
|
||||||
|
|
||||||
|
my @callstack;
|
||||||
|
my %function_info;
|
||||||
|
my $tree = {};
|
||||||
|
my $total_cost = 0;
|
||||||
|
my %opts;
|
||||||
|
|
||||||
|
getopt('f:o:', \%opts);
|
||||||
|
|
||||||
|
my $infd;
|
||||||
|
usage() unless ($opts{'f'} && ($infd = IO::File->new($opts{'f'}, "r")));
|
||||||
|
|
||||||
|
my $outfd;
|
||||||
|
my $outfile = $opts{'o'};
|
||||||
|
unless($outfile) {
|
||||||
|
$opts{'f'} =~ m!([^/]+)$!;
|
||||||
|
$outfile = "cachegrind.out.$1";
|
||||||
|
}
|
||||||
|
$outfd = new IO::File $outfile, "w";
|
||||||
|
usage() unless defined $outfd;
|
||||||
|
|
||||||
|
while(<$infd>) {
|
||||||
|
last if /^PART2/;
|
||||||
|
}
|
||||||
|
while(<$infd>) {
|
||||||
|
chomp;
|
||||||
|
my @args = split;
|
||||||
|
if($args[0] eq '@') {
|
||||||
|
# record timing event
|
||||||
|
my $call_element = pop @callstack;
|
||||||
|
if($call_element) {
|
||||||
|
$call_element->{'cost'} += $args[3];
|
||||||
|
$call_element->{'cumm_cost'} += $args[3];
|
||||||
|
$total_cost += $args[3];
|
||||||
|
push @callstack, $call_element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elsif($args[0] eq '&') {
|
||||||
|
# declare function
|
||||||
|
$function_info{$args[1]}->{'package'} = $args[2];
|
||||||
|
if($args[2] ne 'main') {
|
||||||
|
$function_info{$args[1]}->{'name'} = $args[2]."::".$args[3];
|
||||||
|
} else {
|
||||||
|
$function_info{$args[1]}->{'name'} = $args[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elsif($args[0] eq '+') {
|
||||||
|
# push myself onto the stack
|
||||||
|
my $call_element = { 'specifier' => $args[1], 'cost' => 0 };
|
||||||
|
push @callstack, $call_element;
|
||||||
|
}
|
||||||
|
elsif($args[0] eq '-') {
|
||||||
|
my $called = pop @callstack;
|
||||||
|
my $called_id = $called->{'specifier'};
|
||||||
|
my $caller = pop @callstack;
|
||||||
|
if (exists $tree->{$called_id}) {
|
||||||
|
$tree->{$called_id}->{'cost'} += $called->{'cost'};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$tree->{$called_id} = $called;
|
||||||
|
}
|
||||||
|
if($caller) {
|
||||||
|
$caller->{'child_calls'}++;
|
||||||
|
my $caller_id = $caller->{'specifier'};
|
||||||
|
if(! exists $tree->{$caller_id} ) {
|
||||||
|
$tree->{$caller_id} = { 'specifier' => $caller_id, 'cost' => 0 };
|
||||||
|
# $tree->{$caller_id} = $caller;
|
||||||
|
}
|
||||||
|
$caller->{'cumm_cost'} += $called->{'cumm_cost'};
|
||||||
|
$tree->{$caller_id}->{'called_funcs'}->[$tree->{$caller_id}->{'call_counter'}++]->{$called_id} += $called->{'cumm_cost'};
|
||||||
|
push @callstack, $caller;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elsif($args[0] eq '*') {
|
||||||
|
# goto &func
|
||||||
|
# replace last caller with self
|
||||||
|
my $call_element = pop @callstack;
|
||||||
|
$call_element->{'specifier'} = $args[1];
|
||||||
|
push @callstack, $call_element;
|
||||||
|
}
|
||||||
|
else {print STDERR "Unexpected line: $_\n";}
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Generate output
|
||||||
|
#
|
||||||
|
my $output = '';
|
||||||
|
$output .= "events: Tick\n";
|
||||||
|
$output .= "summary: $total_cost\n";
|
||||||
|
$output .= "cmd: your script\n\n";
|
||||||
|
foreach my $specifier ( keys %$tree ) {
|
||||||
|
my $caller_package = $function_info{$specifier}->{'package'} || '???';
|
||||||
|
my $caller_name = $function_info{$specifier}->{'name'} || '???';
|
||||||
|
my $include = find_include($caller_package);
|
||||||
|
$output .= "ob=\n";
|
||||||
|
$output .= sprintf "fl=%s\n", find_include($caller_package);
|
||||||
|
$output .= sprintf "fn=%s\n", $caller_name;
|
||||||
|
$output .= sprintf "1 %d\n", $tree->{$specifier}->{'cost'};
|
||||||
|
if(exists $tree->{$specifier}->{'called_funcs'}) {
|
||||||
|
foreach my $items (@{$tree->{$specifier}->{'called_funcs'}}) {
|
||||||
|
while(my ($child_specifier, $costs) = each %$items) {
|
||||||
|
$output .= sprintf "cfn=%s\n", $function_info{$child_specifier}->{'name'};
|
||||||
|
$output .= sprintf "cfi=%s\n", find_include($function_info{$child_specifier}->{'package'});
|
||||||
|
$output .= "calls=1\n";
|
||||||
|
$output .= sprintf "1 %d\n", $costs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$output .= "\n";
|
||||||
|
}
|
||||||
|
print STDERR "Writing kcachegrind output to $outfile\n";
|
||||||
|
$outfd->print($output);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub find_include {
|
||||||
|
my $module = shift;
|
||||||
|
$module =~ s!::!/!g;
|
||||||
|
for (@INC) {
|
||||||
|
if ( -f "$_/$module.pm" ) {
|
||||||
|
return "$_/$module.pm";
|
||||||
|
}
|
||||||
|
if ( -f "$_/$module.so" ) {
|
||||||
|
return "$_/$module.so";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub usage() {
|
||||||
|
print STDERR "dprof2calltree -f <tmon.out> [-o outfile]\n";
|
||||||
|
exit -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# vim: set sts=2 ts=2 bs ai expandtab :
|
394
kcachegrind/converters/hotshot2calltree.cmake
Normal file
394
kcachegrind/converters/hotshot2calltree.cmake
Normal file
|
@ -0,0 +1,394 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# _*_ coding: latin1 _*_
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (c) 2003 by WEB.DE, Karlsruhe
|
||||||
|
# Autor: Jörg Beyer <job@webde-ag.de>
|
||||||
|
#
|
||||||
|
# hotshot2cachegrind is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public
|
||||||
|
# License as published by the Free Software Foundation, version 2.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; see the file COPYING. If not, write to
|
||||||
|
# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
# Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# This script transforms the pstat output of the hotshot
|
||||||
|
# python profiler into the input of kcachegrind.
|
||||||
|
#
|
||||||
|
# example usage:
|
||||||
|
# modify you python script to run this code:
|
||||||
|
#
|
||||||
|
# import hotshot
|
||||||
|
# filename = "pythongrind.prof"
|
||||||
|
# prof = hotshot.Profile(filename, lineevents=1)
|
||||||
|
# prof.runcall(run) # assuming that "run" should be called.
|
||||||
|
# prof.close()
|
||||||
|
#
|
||||||
|
# it will run the "run"-method under profiling and write
|
||||||
|
# the results in a file, called "pythongrind.prof".
|
||||||
|
#
|
||||||
|
# then call this script:
|
||||||
|
# hotshot2cachegrind -o <output> <input>
|
||||||
|
# or here:
|
||||||
|
# hotshot2cachegrind cachegrind.out.0 pythongrind.prof
|
||||||
|
#
|
||||||
|
# then call kcachegrind:
|
||||||
|
# kcachegrind cachegrind.out.0
|
||||||
|
#
|
||||||
|
# TODO:
|
||||||
|
# * es gibt Probleme mit rekursiven (direkt und indirekt) Aufrufen - dann
|
||||||
|
# stimmen die Kosten nicht.
|
||||||
|
#
|
||||||
|
# * einige Funktionen werden mit "?" als Name angezeigt. Evtl sind
|
||||||
|
# das nur die C/C++ extensions.
|
||||||
|
#
|
||||||
|
# * es fehlt noch ein Funktionsnamen Mangling, dass die Filenamen berücksichtigt,
|
||||||
|
# zZ sind alle __init__'s und alle run's schwer unterscheidbar :-(
|
||||||
|
#
|
||||||
|
version = "Version ${KCACHEGRIND_VERSION}"
|
||||||
|
progname = "hotshot2cachegrind"
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
from hotshot import stats,log
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
file_limit=0
|
||||||
|
|
||||||
|
what2text = {
|
||||||
|
log.WHAT_ADD_INFO : "ADD_INFO",
|
||||||
|
log.WHAT_DEFINE_FUNC : "DEFINE_FUNC",
|
||||||
|
log.WHAT_DEFINE_FILE : "DEFINE_FILE",
|
||||||
|
log.WHAT_LINENO : "LINENO",
|
||||||
|
log.WHAT_EXIT : "EXIT",
|
||||||
|
log.WHAT_ENTER : "ENTER"}
|
||||||
|
|
||||||
|
# a pseudo caller on the caller stack. This represents
|
||||||
|
# the Python interpreter that executes the given python
|
||||||
|
# code.
|
||||||
|
root_caller = ("PythonInterpreter",0,"execute")
|
||||||
|
|
||||||
|
class CallStack:
|
||||||
|
"""A tiny Stack implementation, based on python lists"""
|
||||||
|
def __init__(self):
|
||||||
|
self.stack = []
|
||||||
|
self.recursion_counter = {}
|
||||||
|
def push(self, elem):
|
||||||
|
"""put something on the stack"""
|
||||||
|
self.stack.append(elem)
|
||||||
|
rc = self.recursion_counter.get(elem, 0)
|
||||||
|
self.recursion_counter[elem] = rc + 1
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
"""get the head element of the stack and remove it from the stack"""
|
||||||
|
elem = self.stack[-1:][0]
|
||||||
|
rc = self.recursion_counter.get(elem) - 1
|
||||||
|
if rc>0:
|
||||||
|
self.recursion_counter[elem] = rc
|
||||||
|
else:
|
||||||
|
del self.recursion_counter[elem]
|
||||||
|
return self.stack.pop()
|
||||||
|
|
||||||
|
def top(self):
|
||||||
|
"""get the head element of the stack, stack is unchanged."""
|
||||||
|
return self.stack[-1:][0]
|
||||||
|
def handleLineCost(self, tdelta):
|
||||||
|
p, c = self.stack.pop()
|
||||||
|
self.stack.append( (p,c + tdelta) )
|
||||||
|
def size(self):
|
||||||
|
""" return how many elements the stack has"""
|
||||||
|
return len(self.stack)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "[stack: %s]" % self.stack
|
||||||
|
|
||||||
|
def recursion(self, pos):
|
||||||
|
return self.recursion_counter.get(pos, 0)
|
||||||
|
#return self.recursion_dict.has_key((entry[0][0], entry[0][2]))
|
||||||
|
|
||||||
|
def return_from_call(caller_stack, call_dict, cost_now):
|
||||||
|
"""return from a function call
|
||||||
|
remove the function from the caller stack,
|
||||||
|
add the costs to the calling function.
|
||||||
|
"""
|
||||||
|
called, cost_at_enter = caller_stack.pop()
|
||||||
|
caller, caller_cost = caller_stack.top()
|
||||||
|
|
||||||
|
#print "return_from_call: %s ruft %s" % (caller, called,)
|
||||||
|
|
||||||
|
per_file_dict = call_dict.get(called[0], {})
|
||||||
|
per_caller_dict = per_file_dict.get(called[2], {})
|
||||||
|
cost_so_far, call_counter = per_caller_dict.get(caller, (0, 0))
|
||||||
|
|
||||||
|
if caller_stack.recursion(called):
|
||||||
|
per_caller_dict[caller] = (cost_so_far, call_counter + 1)
|
||||||
|
else:
|
||||||
|
per_caller_dict[caller] = (cost_so_far + cost_now - cost_at_enter, call_counter + 1)
|
||||||
|
|
||||||
|
per_file_dict[called[2]] = per_caller_dict
|
||||||
|
call_dict[called[0]] = per_file_dict
|
||||||
|
|
||||||
|
|
||||||
|
def updateStatus(filecount):
|
||||||
|
sys.stdout.write("reading File #%d \r" % filecount)
|
||||||
|
sys.stdout.flush()
|
||||||
|
def convertProfFiles(output, inputfilenames):
|
||||||
|
"""convert all the given input files into one kcachegrind
|
||||||
|
input file.
|
||||||
|
"""
|
||||||
|
call_dict = {}
|
||||||
|
cost_per_pos = {}
|
||||||
|
cost_per_function = {}
|
||||||
|
caller_stack = CallStack()
|
||||||
|
caller_stack.push((root_caller, 0))
|
||||||
|
|
||||||
|
total_cost = 0
|
||||||
|
filecount = 1
|
||||||
|
number_of_files = len(inputfilenames)
|
||||||
|
for inputfilename in inputfilenames:
|
||||||
|
updateStatus(filecount)
|
||||||
|
cost, filecount = convertHandleFilename(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount)
|
||||||
|
total_cost += cost
|
||||||
|
if (file_limit > 0) and (filecount > file_limit):
|
||||||
|
break
|
||||||
|
|
||||||
|
print
|
||||||
|
print "total_cost: % d Ticks",total_cost
|
||||||
|
dumpResults(output, call_dict, total_cost, cost_per_pos, cost_per_function)
|
||||||
|
|
||||||
|
def convertHandleFilename(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount):
|
||||||
|
updateStatus(filecount)
|
||||||
|
if not ((file_limit > 0) and (filecount > file_limit)):
|
||||||
|
if os.path.isdir(inputfilename):
|
||||||
|
cost, filecount = convertProfDir(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount)
|
||||||
|
elif os.path.isfile(inputfilename):
|
||||||
|
cost = convertProfFile(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function)
|
||||||
|
filecount += 1
|
||||||
|
else:
|
||||||
|
sys.stderr.write("warn: ignoring '%s', is no file and no directory\n" % inputfilename)
|
||||||
|
cost = 0
|
||||||
|
return (cost, filecount)
|
||||||
|
|
||||||
|
def convertProfDir(start, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount):
|
||||||
|
cost = 0
|
||||||
|
filenames = os.listdir(start)
|
||||||
|
for f in filenames:
|
||||||
|
if (file_limit > 0) and (filecount > file_limit):
|
||||||
|
break
|
||||||
|
full = os.path.join(start, f)
|
||||||
|
c, filecount = convertHandleFilename(full, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount)
|
||||||
|
cost += c;
|
||||||
|
return (cost, filecount)
|
||||||
|
|
||||||
|
def handleCostPerPos(cost_per_pos, pos, current_cost):
|
||||||
|
"""
|
||||||
|
the cost per source position are managed in a dict in a dict.
|
||||||
|
|
||||||
|
the cost are handled per file and there per function.
|
||||||
|
so, the per-file-dict contains some per-function-dicts
|
||||||
|
which sum up the cost per line (in this function and in
|
||||||
|
this file).
|
||||||
|
"""
|
||||||
|
filename = pos[0]
|
||||||
|
lineno = pos[1]
|
||||||
|
funcname = pos[2]
|
||||||
|
file_dict = cost_per_pos.get(filename, {})
|
||||||
|
func_dict = file_dict.get(funcname, {})
|
||||||
|
func_dict.setdefault(lineno, 0)
|
||||||
|
func_dict[lineno] += current_cost
|
||||||
|
file_dict[funcname] = func_dict
|
||||||
|
cost_per_pos[filename] = file_dict
|
||||||
|
|
||||||
|
def convertProfFile(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function):
|
||||||
|
"""convert a single input file into one kcachegrind
|
||||||
|
data.
|
||||||
|
|
||||||
|
this is the most expensive function in this python source :-)
|
||||||
|
"""
|
||||||
|
|
||||||
|
total_cost = 0
|
||||||
|
try:
|
||||||
|
logreader = log.LogReader(inputfilename)
|
||||||
|
current_cost = 0
|
||||||
|
hc = handleCostPerPos # shortcut
|
||||||
|
for item in logreader:
|
||||||
|
what, pos ,tdelta = item
|
||||||
|
(file, lineno, func) = pos
|
||||||
|
#line = "%s %s %d %s %d" % (what2text[what], file, lineno, func, tdelta)
|
||||||
|
#print line
|
||||||
|
# most common cases first
|
||||||
|
if what == log.WHAT_LINENO:
|
||||||
|
# add the current cost to the current function
|
||||||
|
hc(cost_per_pos, pos, tdelta)
|
||||||
|
total_cost += tdelta
|
||||||
|
elif what == log.WHAT_ENTER:
|
||||||
|
caller_stack.push((pos, total_cost))
|
||||||
|
hc(cost_per_pos, pos, tdelta)
|
||||||
|
total_cost += tdelta
|
||||||
|
elif what == log.WHAT_EXIT:
|
||||||
|
hc(cost_per_pos, pos, tdelta)
|
||||||
|
total_cost += tdelta
|
||||||
|
return_from_call(caller_stack, call_dict, total_cost)
|
||||||
|
else:
|
||||||
|
assert 0, "duh: %d" % what
|
||||||
|
|
||||||
|
|
||||||
|
# I have no idea, why sometimes the stack is not empty - we
|
||||||
|
# have to rewind the stack to get 100% for the root_caller
|
||||||
|
while caller_stack.size() > 1:
|
||||||
|
return_from_call(caller_stack, call_dict, total_cost)
|
||||||
|
|
||||||
|
except IOError:
|
||||||
|
print "could not open inputfile '%s', ignore this." % inputfilename
|
||||||
|
except EOFError, m:
|
||||||
|
print "EOF: %s" % (m,)
|
||||||
|
return total_cost
|
||||||
|
|
||||||
|
def pretty_name(file, function):
|
||||||
|
#pfile = os.path.splitext(os.path.basename(file)) [0]
|
||||||
|
#return "%s_[%s]" % (function, file)
|
||||||
|
return "%s" % function
|
||||||
|
#return "%s::%s" % (file, function)
|
||||||
|
#return "%s_%s" % (pfile, function)
|
||||||
|
|
||||||
|
class TagWriter:
|
||||||
|
def __init__(self, output):
|
||||||
|
self.output = output
|
||||||
|
self.last_values = {}
|
||||||
|
|
||||||
|
def clearTag(self, tag):
|
||||||
|
if self.last_values.has_key(tag):
|
||||||
|
del self.last_values[ tag ]
|
||||||
|
def clear(self):
|
||||||
|
self.last_values = {}
|
||||||
|
|
||||||
|
def write(self, tag, value):
|
||||||
|
self.output.write("%s=%s\n" % (tag, value))
|
||||||
|
#if (not self.last_values.has_key(tag)) or self.last_values[tag] != value:
|
||||||
|
# self.last_values[ tag ] = value
|
||||||
|
# self.output.write("%s=%s\n" % (tag, value))
|
||||||
|
|
||||||
|
def dumpResults(output, call_dict, total_cost, cost_per_pos, cost_per_function):
|
||||||
|
"""write the collected results in the format kcachegrind
|
||||||
|
could read.
|
||||||
|
"""
|
||||||
|
# the intro
|
||||||
|
output.write("events: Tick\n")
|
||||||
|
output.write("summary: %d\n" % total_cost)
|
||||||
|
output.write("cmd: your python script\n")
|
||||||
|
output.write("\n")
|
||||||
|
tagwriter = TagWriter(output)
|
||||||
|
|
||||||
|
# now the costs per line
|
||||||
|
for file in cost_per_pos.keys():
|
||||||
|
func_dict = cost_per_pos[file]
|
||||||
|
for func in func_dict.keys():
|
||||||
|
line_dict = func_dict[func]
|
||||||
|
tagwriter.write("ob", file)
|
||||||
|
tagwriter.write("fn", func)# pretty_name(file, func)) ; output.write("# ^--- 2\n")
|
||||||
|
tagwriter.write("fl", file)
|
||||||
|
for line in line_dict:
|
||||||
|
output.write("%d %d\n" %( line, line_dict[line] ))
|
||||||
|
|
||||||
|
output.write("\n\n")
|
||||||
|
# now the function calls. For each caller all the called
|
||||||
|
# functions and their costs are written.
|
||||||
|
for file in call_dict.keys():
|
||||||
|
per_file_dict = call_dict[file]
|
||||||
|
#print "file %s -> %s" % (file, per_file_dict)
|
||||||
|
for called_x in per_file_dict.keys():
|
||||||
|
#print "called_x:",called_x
|
||||||
|
per_caller_dict = per_file_dict[called_x]
|
||||||
|
#print "called_x %s wird gerufen von: %s" % (called_x, per_caller_dict)
|
||||||
|
for caller_x in per_caller_dict.keys():
|
||||||
|
tagwriter.write("ob", caller_x[0])
|
||||||
|
tagwriter.write("fn", caller_x[2])# pretty_name(caller_x[2], caller_x[0])) ; output.write("# ^--- 1\n")
|
||||||
|
tagwriter.write("fl", caller_x[0])
|
||||||
|
tagwriter.write("cob", file)
|
||||||
|
tagwriter.write("cfn", called_x) #pretty_name(file, called_x))
|
||||||
|
tagwriter.write("cfl", file)
|
||||||
|
cost, count = per_caller_dict[caller_x]
|
||||||
|
#print "called_x:",called_x
|
||||||
|
output.write("calls=%d\n%d %d\n" % (count, caller_x[1], cost))
|
||||||
|
tagwriter.clear()
|
||||||
|
#tagwriter.clearTag("cob")
|
||||||
|
# is it a bug in kcachegrind, that the "cob=xxx" line has
|
||||||
|
# to be rewritten after a calls entry with costline ?
|
||||||
|
#assert cost <= total_cost, "caller_x: %s, per_caller_dict: %s " % (caller_x, per_caller_dict, )
|
||||||
|
#output.write("calls=%d\n%d %d\n" % (count, caller_x[1], cost))
|
||||||
|
output.write("\n")
|
||||||
|
|
||||||
|
def run_without_optparse():
|
||||||
|
"""parse the options without optparse, use sys.argv"""
|
||||||
|
if len(sys.argv) < 4 or sys.argv[1] != "-o" :
|
||||||
|
print "usage: hotshot2cachegrind -o outputfile in1 [in2 [in3 [...]]]"
|
||||||
|
return
|
||||||
|
outputfilename = sys.argv[2]
|
||||||
|
try:
|
||||||
|
output = file(outputfilename, "w")
|
||||||
|
args = sys.argv[3:]
|
||||||
|
convertProfFiles(output, args)
|
||||||
|
output.close()
|
||||||
|
except IOError:
|
||||||
|
print "could not open '%s' for writing." % outputfilename
|
||||||
|
|
||||||
|
def run_with_optparse():
|
||||||
|
"""parse the options with optparse"""
|
||||||
|
|
||||||
|
global file_limit
|
||||||
|
|
||||||
|
versiontext = "%s version: %s" % ( progname, version.split()[1], )
|
||||||
|
parser = OptionParser(version=versiontext)
|
||||||
|
parser.add_option("-o", "--output",
|
||||||
|
action="store", type="string", dest="outputfilename",
|
||||||
|
help="write output into FILE")
|
||||||
|
parser.add_option("--file-limit",
|
||||||
|
action="store", dest="file_limit", default=0,
|
||||||
|
help="stop after given number of input files")
|
||||||
|
output = sys.stdout
|
||||||
|
close_output = 0
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
file_limit = int(options.file_limit)
|
||||||
|
try:
|
||||||
|
if options.outputfilename and options.outputfilename != "-":
|
||||||
|
output = file(options.outputfilename, "w")
|
||||||
|
close_output = 1
|
||||||
|
except IOError:
|
||||||
|
print "could not open '%s' for writing." % options.outputfilename
|
||||||
|
if output:
|
||||||
|
convertProfFiles(output, args)
|
||||||
|
if close_output:
|
||||||
|
output.close()
|
||||||
|
|
||||||
|
|
||||||
|
def profile_myself():
|
||||||
|
import hotshot
|
||||||
|
filename = "self.prof"
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
prof = hotshot.Profile(filename, lineevents=1)
|
||||||
|
prof.runcall(run)
|
||||||
|
prof.close()
|
||||||
|
else:
|
||||||
|
print "not profiling myself, since '%s' exists, running normal" % filename
|
||||||
|
run()
|
||||||
|
|
||||||
|
# check if optparse is available.
|
||||||
|
try:
|
||||||
|
from optparse import OptionParser
|
||||||
|
run = run_with_optparse
|
||||||
|
except ImportError:
|
||||||
|
run = run_without_optparse
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
run()
|
||||||
|
#profile_myself()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit(1)
|
38
kcachegrind/converters/memprof2calltree
Executable file
38
kcachegrind/converters/memprof2calltree
Executable file
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
#
|
||||||
|
# Convert the memory profiles of memprof to calltree format,
|
||||||
|
# loadable with KCachegrind
|
||||||
|
#
|
||||||
|
# (C) 2004, Josef Weidendorfer
|
||||||
|
|
||||||
|
print "events: Allocated\n";
|
||||||
|
|
||||||
|
while(<>) {
|
||||||
|
if (/^(\S.*)$/) {
|
||||||
|
$next = 0;
|
||||||
|
print "\nfn=$1\n";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (/^ children:/) {
|
||||||
|
$next = 1; #children
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (/^ inherited:/) {
|
||||||
|
$next = 2; #inherited
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (/^ total:/) {
|
||||||
|
# ignore, is calculated
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (/^ self:\s*(\d+)/) {
|
||||||
|
if ($1 ne "0") {
|
||||||
|
print "0 $1\n";
|
||||||
|
}
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (/^\s+(\S.*?):\s*(\d+)$/) {
|
||||||
|
if ($next < 2) { next; }
|
||||||
|
print "cfn=$1\ncalls=0 0\n0 $2\n";
|
||||||
|
}
|
||||||
|
}
|
238
kcachegrind/converters/op2calltree
Executable file
238
kcachegrind/converters/op2calltree
Executable file
|
@ -0,0 +1,238 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
#
|
||||||
|
# Copyright (c) 2004
|
||||||
|
# Author: Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
#
|
||||||
|
# op2calltree is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public
|
||||||
|
# License as published by the Free Software Foundation, version 2.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; see the file COPYING. If not, write to
|
||||||
|
# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
# Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Converter from OProfile's output of "opreport -gdf" (v 0.8)
|
||||||
|
# into callgrind format.
|
||||||
|
#
|
||||||
|
# Generate a OProfile report with opreport and flags -gdf
|
||||||
|
# and pipe this as standard input into this script.
|
||||||
|
# This will generate separate cachegrind files for every application.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
# parse symbol line. example (with 1 event type, $has_image==0):
|
||||||
|
# 308 0.1491 /path/source.c:6 /path/app main
|
||||||
|
sub parseSymSpec {
|
||||||
|
$e = 0;
|
||||||
|
while($e < $eventCount) {
|
||||||
|
($line) = ($line =~ /\d+\s+\S+\s+(.*)/);
|
||||||
|
$e++;
|
||||||
|
}
|
||||||
|
if ($line =~ s/^\(no location information\)\s+//) {
|
||||||
|
$file = "???";
|
||||||
|
$linenr = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
($file,$linenr) = ($line =~ s/(\S+?):(\d+)\s+//);
|
||||||
|
}
|
||||||
|
if ($has_image) {
|
||||||
|
if ($line =~ s/^(\S+)\s+//) { $img = $1; }
|
||||||
|
}
|
||||||
|
if ($has_app) {
|
||||||
|
if ($line =~ s/^(\S+)\s+//) { $app = $1; }
|
||||||
|
if (!$has_image) { $img = $app; }
|
||||||
|
}
|
||||||
|
$sym = $line;
|
||||||
|
|
||||||
|
$app =~ s/^.*\///;
|
||||||
|
if ($sym eq "(no symbols)") { $sym = "???"; }
|
||||||
|
$file{$sym} = $file;
|
||||||
|
$linenr{$sym} = $linenr;
|
||||||
|
$app{$sym} = $app;
|
||||||
|
$img{$app,$sym} = $img;
|
||||||
|
$syms{$app}++;
|
||||||
|
|
||||||
|
if ($app ne $oldApp) {
|
||||||
|
$oldApp = $app;
|
||||||
|
print "\n\nApp $app\n";
|
||||||
|
}
|
||||||
|
print " Symbol $sym (Image $img)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$eventCount = 0;
|
||||||
|
$descCount = 0;
|
||||||
|
$lnr = 0;
|
||||||
|
$has_image = 0;
|
||||||
|
$has_app = 0;
|
||||||
|
$app = "unnamed";
|
||||||
|
$img = "???";
|
||||||
|
|
||||||
|
# first loop till first symbol specification
|
||||||
|
while(<>) {
|
||||||
|
$lnr++;
|
||||||
|
chomp;
|
||||||
|
if (/^CPU:/) {
|
||||||
|
$desc[$descCount++] = $_;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (/^Counted\s*(\S+)/) {
|
||||||
|
$desc[$descCount++] = $_;
|
||||||
|
$eventCount++;
|
||||||
|
$events[$eventCount] = $1;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (/^(Profiling through timer.*)/) {
|
||||||
|
$desc[$descCount++] = $_;
|
||||||
|
$eventCount++;
|
||||||
|
$events[$eventCount] = "Timer";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (/^vma/) {
|
||||||
|
# title row: adapt to separation options of OProfile
|
||||||
|
if (/image/) { $has_image = 1; }
|
||||||
|
if (/app/) { $has_app = 1; }
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (/^([0-9a-fA-F]+)\s*(.*)$/) {
|
||||||
|
$vmaSym = $1;
|
||||||
|
$line = $2;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($eventCount == 0) {
|
||||||
|
die "No Events found";
|
||||||
|
}
|
||||||
|
|
||||||
|
print "Description:\n";
|
||||||
|
foreach $d (@desc) { print " $d\n"; }
|
||||||
|
print "\n";
|
||||||
|
|
||||||
|
print "Events:";
|
||||||
|
foreach $e (@events) { print " $e"; }
|
||||||
|
print "\n";
|
||||||
|
|
||||||
|
parseSymSpec;
|
||||||
|
|
||||||
|
while(<>) {
|
||||||
|
$lnr++;
|
||||||
|
if (/^([0-9a-fA-F]+)\s*(.*)$/) {
|
||||||
|
$vmaSym = $1;
|
||||||
|
$line = $2;
|
||||||
|
|
||||||
|
parseSymSpec;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (/^\s+([0-9a-fA-F]+)\s*(.*)$/) {
|
||||||
|
|
||||||
|
$sampleCount{$app,$sym}++;
|
||||||
|
$sc = $sampleCount{$app,$sym};
|
||||||
|
|
||||||
|
$vma{$app,$sym,$sc} = $1;
|
||||||
|
$line = $2;
|
||||||
|
|
||||||
|
$e = 1;
|
||||||
|
while($e <= $eventCount) {
|
||||||
|
($cost, $line) = ($line =~ /(\d+)\s+\S+\s+(.*)/);
|
||||||
|
$summary{$app,$e} += $cost;
|
||||||
|
$cost{"$app,$sym,$sc,$e"} = $cost;
|
||||||
|
$e++;
|
||||||
|
}
|
||||||
|
if ($line =~ /\(no location information\)/) {
|
||||||
|
$file = "???";
|
||||||
|
$linenr = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
($file,$linenr) = ($line =~ /(\S+?):(\d+)/);
|
||||||
|
}
|
||||||
|
$sFile{$app,$sym,$sc} = $file;
|
||||||
|
$linenr{$app,$sym,$sc} = $linenr;
|
||||||
|
|
||||||
|
$file =~ s/^.*\///;
|
||||||
|
print " Sample $sc: $vma{$app,$sym,$sc} ($file:$linenr):";
|
||||||
|
foreach $e (1 .. $eventCount) { $c = $cost{"$app,$sym,$sc,$e"} ; print " $c"; }
|
||||||
|
print "\n";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
die "ERROR: Reading line $lnr '$_'\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach $app (keys %syms) {
|
||||||
|
if ($app eq "") { next; }
|
||||||
|
print "Generating dump for App '$app'...\n";
|
||||||
|
|
||||||
|
$out = "# Generated by op2cg, using OProfile with opreport -gdf\n";
|
||||||
|
$out .= "positions: instr line\n";
|
||||||
|
|
||||||
|
$out .= "events:";
|
||||||
|
foreach $e (@events) { $out .= " $e"; }
|
||||||
|
$out .= "\n";
|
||||||
|
|
||||||
|
$out .= "summary:";
|
||||||
|
foreach $e (1 .. $eventCount) { $out .= " $summary{$app,$e}"; }
|
||||||
|
$out .= "\n\n";
|
||||||
|
|
||||||
|
%fileNum = ();
|
||||||
|
$fileNum = 1;
|
||||||
|
$sf = "";
|
||||||
|
|
||||||
|
$img = "";
|
||||||
|
|
||||||
|
foreach $sym (keys %file) {
|
||||||
|
if ($sampleCount{$app,$sym} eq "") { next; }
|
||||||
|
|
||||||
|
if ($img{$app,$sym} ne $img) {
|
||||||
|
$img = $img{$app,$sym};
|
||||||
|
$out .= "ob=$img\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $file{$sym};
|
||||||
|
if ($sf ne $file) {
|
||||||
|
if ($fileNum{$file} eq "") {
|
||||||
|
$fileNum{$file} = $fileNum;
|
||||||
|
$out .= "fl=($fileNum) $file\n";
|
||||||
|
$fileNum++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$out .= "fl=($fileNum{$file})\n";
|
||||||
|
}
|
||||||
|
$sf = $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
$out .= "fn=$sym\n";
|
||||||
|
foreach $sc (1 .. $sampleCount{$app,$sym}) {
|
||||||
|
if ($sf ne $sFile{$app,$sym,$sc}) {
|
||||||
|
$sf = $sFile{$app,$sym,$sc};
|
||||||
|
if ($sf eq $file) {
|
||||||
|
$out .= "fe=($fileNum{$file})\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($fileNum{$sf} eq "") {
|
||||||
|
$fileNum{$sf} = $fileNum;
|
||||||
|
$out .= "fi=($fileNum) $sf\n";
|
||||||
|
$fileNum++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$out .= "fi=($fileNum{$sf})\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$out .= "0x$vma{$app,$sym,$sc} $linenr{$app,$sym,$sc}";
|
||||||
|
foreach $e (1 .. $eventCount) { $c = $cost{"$app,$sym,$sc,$e"} ; $out .= " $c"; }
|
||||||
|
$out .= "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open OUT, ">oprof.out.$app";
|
||||||
|
print OUT $out;
|
||||||
|
close OUT;
|
||||||
|
}
|
218
kcachegrind/converters/pprof2calltree
Normal file
218
kcachegrind/converters/pprof2calltree
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# - Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# - Redistributions in binary form must reproduce the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# - All advertising materials mentioning features or use of this software
|
||||||
|
# must display the following acknowledgement: This product includes software
|
||||||
|
# developed by OmniTI Computer Consulting.
|
||||||
|
#
|
||||||
|
# - Neither name of the company nor the names of its contributors may be
|
||||||
|
# used to endorse or promote products derived from this software without
|
||||||
|
# specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS `AS IS'' AND ANY
|
||||||
|
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2004 OmniTI Computer Consulting
|
||||||
|
# All rights reserved
|
||||||
|
# The following code was written by George Schlossnagle <george@omniti.com>
|
||||||
|
# and is provided completely free and without any warranty.
|
||||||
|
#
|
||||||
|
# This script is designed to convert the pprof output from
|
||||||
|
# APD (http://pecl.php.net/apd/) to one readable by kcachegrind. To use
|
||||||
|
# this script:
|
||||||
|
#
|
||||||
|
# 1) Install APD.
|
||||||
|
# 2) Profile your script with APD accordingto the directions in it's
|
||||||
|
# README file.
|
||||||
|
# 3) Take the pprof trace file for your script (pprof.XXXXX.Y) and run it
|
||||||
|
# through this script as follows:
|
||||||
|
# > pprof2calltree -f pprof.12345.1
|
||||||
|
# This creates a new file cachegrind.out.12345.1
|
||||||
|
# 4) View your trace with pprof2calltree cachegrind.out.12345.1
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require "Console/Getopt.php";
|
||||||
|
|
||||||
|
$con = new Console_Getopt;
|
||||||
|
$args = $con->readPHPArgv();
|
||||||
|
array_shift($args);
|
||||||
|
$shortoptions = 'f:';
|
||||||
|
$retval = $con->getopt( $args, $shortoptions);
|
||||||
|
if(is_object($retval)) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
foreach ($retval[0] as $kv_array) {
|
||||||
|
$opt[$kv_array[0]] = $kv_array[1];
|
||||||
|
}
|
||||||
|
if(!$opt['f']) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
if(!file_exists($opt['f'])) {
|
||||||
|
print "Trace file ${opt['f']} does not exist\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$IN = fopen($opt['f'], "r");
|
||||||
|
if(!$IN) {
|
||||||
|
print "Trace file ${opt['f']} could not be opened\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path_parts = pathinfo($opt['f']);
|
||||||
|
$outfile = "cachegrind.out.".$path_parts['basename'];
|
||||||
|
$OUT = fopen($outfile, "w");
|
||||||
|
if(!$OUT) {
|
||||||
|
print "Destination file $outfile could not be opened.\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(($line = fgets($IN)) !== false) {
|
||||||
|
$line = rtrim($line);
|
||||||
|
if($line == "END_HEADER") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$tree = array();
|
||||||
|
$callstack = array();
|
||||||
|
while(($line = fgets($IN)) !== false) {
|
||||||
|
$line = rtrim($line);
|
||||||
|
$args = explode(" ", $line);
|
||||||
|
if($args[0] == '!') {
|
||||||
|
$file_lookup[$args[1]] = $args[2];
|
||||||
|
}
|
||||||
|
else if($args[0] == '&') {
|
||||||
|
$function_lookup[$args[1]] = $args[2];
|
||||||
|
$function_type[$args[1]] = ($args[3] == 2)?"USER":"INTERNAL";
|
||||||
|
}
|
||||||
|
else if($args[0] == '+') {
|
||||||
|
$val = array(function_id => $args[1],
|
||||||
|
file_id => $args[2],
|
||||||
|
line => $args[3],
|
||||||
|
cost => 0);
|
||||||
|
array_push($callstack, $val);
|
||||||
|
}
|
||||||
|
else if($args[0] == '-') {
|
||||||
|
// retrieve $called to discard
|
||||||
|
$called = array_pop($callstack);
|
||||||
|
// retrieve $caller for reference
|
||||||
|
$caller = array_pop($callstack);
|
||||||
|
$called_id = $called['function_id'];
|
||||||
|
|
||||||
|
// Set meta data if not already set'
|
||||||
|
if(!array_key_exists($called_id, $tree)) {
|
||||||
|
$tree[$called_id] = $called;
|
||||||
|
// initialize these to 0
|
||||||
|
$tree[$called_id]['cost_per_line'] = array();
|
||||||
|
}
|
||||||
|
if($caller !== null) {
|
||||||
|
$caller['child_calls']++;
|
||||||
|
$caller_id = $caller['function_id'];
|
||||||
|
if(!array_key_exists($caller_id, $tree)) {
|
||||||
|
$tree[$caller_id] = $caller;
|
||||||
|
}
|
||||||
|
$caller['cost'] += $called['cost'];
|
||||||
|
$tree[$caller_id]['called_funcs'][$tree[$caller_id]['call_counter']++][$called_id][$called['file_id']][$called['line']] += $called['cost'];
|
||||||
|
array_push($callstack, $caller);
|
||||||
|
}
|
||||||
|
if(is_array($called['cost_per_line'])) {
|
||||||
|
foreach($called[cost_per_line] as $file => $lines) {
|
||||||
|
foreach($lines as $line => $cost) {
|
||||||
|
$tree[$called_id]['cost_per_line'][$file][$line] += $cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if($args[0] == '@') {
|
||||||
|
$called = array_pop($callstack);
|
||||||
|
switch(count($args)) {
|
||||||
|
// support new and old-style pprof data
|
||||||
|
case 6:
|
||||||
|
$file = $args[1];
|
||||||
|
$line = $args[2];
|
||||||
|
$real_tm = $args[5];
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
$file = $called['file_id'];
|
||||||
|
$line = $called['line'];
|
||||||
|
$real_tm = $args[3];
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
$called['cost_per_line'][$file][$line] += $real_tm;
|
||||||
|
$called['cost'] += $real_tm;
|
||||||
|
$total_cost += $real_tm;
|
||||||
|
array_push($callstack, $called);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
print "events: Tick\n";
|
||||||
|
print "summary: $total_cost\n";
|
||||||
|
printf("cmd: %s\n", $file_lookup[1]);
|
||||||
|
print "\n";
|
||||||
|
|
||||||
|
foreach($tree as $caller => $data) {
|
||||||
|
$filename = $file_lookup[$data['file_id']]?$file_lookup[$data['file_id']]:"???";
|
||||||
|
printf("ob=%s\n", $function_type[$caller]);
|
||||||
|
printf("fl=%s\n", $filename);
|
||||||
|
printf("fn=%s\n", $function_lookup[$caller]);
|
||||||
|
if(is_array($data['cost_per_line'])) {
|
||||||
|
foreach($data['cost_per_line'] as $file => $lines) {
|
||||||
|
foreach($lines as $line => $cost) {
|
||||||
|
print "$line $cost\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ($data['cost']) {
|
||||||
|
printf("COST %s %s\n", $items['line'], $items['cost']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print_r($items);
|
||||||
|
}
|
||||||
|
if(is_array($data['called_funcs'])) {
|
||||||
|
foreach($data['called_funcs'] as $counter => $items) {
|
||||||
|
foreach($items as $called_id => $costs) {
|
||||||
|
if(is_array($costs)) {
|
||||||
|
printf("cfn=%s\n", $function_lookup[$called_id]);
|
||||||
|
foreach($costs as $file => $lines) {
|
||||||
|
printf("cfi=%s\ncalls=1\n", $file_lookup[$file]);
|
||||||
|
foreach($lines as $line => $cost) {
|
||||||
|
print "$line $cost\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print "\n";
|
||||||
|
}
|
||||||
|
print "\ntotals=$total_cost\n";
|
||||||
|
$buffer = ob_get_clean();
|
||||||
|
print "Writing kcachegrind compatible output to $outfile\n";
|
||||||
|
fwrite($OUT, $buffer);
|
||||||
|
|
||||||
|
function usage()
|
||||||
|
{
|
||||||
|
print <<<EOD
|
||||||
|
pprof2calltree -f <tracefile>
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
?>
|
11
kcachegrind/kcachegrind.lsm.cmake
Normal file
11
kcachegrind/kcachegrind.lsm.cmake
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Begin3
|
||||||
|
Title: kcachegrind
|
||||||
|
Version: ${KCACHEGRIND_VERSION}
|
||||||
|
Description: KDE Profiling Visualisation Tool
|
||||||
|
Keywords: Profiling, Performance Analysis, Visualisation, Development
|
||||||
|
Author: Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
Maintained-by: Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
Home-page: http://kcachegrind.sourceforge.net
|
||||||
|
Platforms: Linux and other Unices
|
||||||
|
Copying-policy: GNU Public License
|
||||||
|
End
|
55
kcachegrind/kcachegrind.spec.cmake
Normal file
55
kcachegrind/kcachegrind.spec.cmake
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
Summary: KDE Profiling Visualisation Tool
|
||||||
|
Name: kcachegrind
|
||||||
|
Version: ${KCACHEGRIND_VERSION}
|
||||||
|
Release: 1
|
||||||
|
Copyright: GPL
|
||||||
|
Group: Development/Tools
|
||||||
|
Vendor: (none)
|
||||||
|
URL: http://kcachegrind.sourceforge.net
|
||||||
|
Packager: Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
Source: kcachegrind-${KCACHEGRIND_VERSION}.tar.gz
|
||||||
|
BuildRoot: /var/tmp/build
|
||||||
|
|
||||||
|
%description
|
||||||
|
KCachegrind is a GPL'd tool for quick browsing in and visualisation
|
||||||
|
of performance data of an application run. This data is produced by
|
||||||
|
profiling tools and typically includes distribution of cost events
|
||||||
|
to source code ranges (instructions, source lines, functions, C++ classes)
|
||||||
|
and call relationship of functions.
|
||||||
|
KCachegrind has a list of functions sorted according to different cost
|
||||||
|
types, and can provide various performance views for a function like
|
||||||
|
direct/indirect callers/callees, TreeMap visualisation of cost distribution
|
||||||
|
among callees, call graph sectors centered around the function and
|
||||||
|
annotated source/assembler.
|
||||||
|
Currently, KCachegrind depends on data delivered by the profiling tool
|
||||||
|
calltree, powered by the Valgrind runtime instrumentation framework.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup
|
||||||
|
CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_FLAGS" ./configure \
|
||||||
|
\
|
||||||
|
$LOCALFLAGS
|
||||||
|
%build
|
||||||
|
# Setup for parallel builds
|
||||||
|
numprocs=`egrep -c ^cpu[0-9]+ /proc/stat || :`
|
||||||
|
if [ "$numprocs" = "0" ]; then
|
||||||
|
numprocs=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
make -j$numprocs
|
||||||
|
|
||||||
|
%install
|
||||||
|
make install-strip DESTDIR=$RPM_BUILD_ROOT
|
||||||
|
|
||||||
|
cd $RPM_BUILD_ROOT
|
||||||
|
find . -type d | sed '1,2d;s,^\.,\%attr(-\,root\,root) \%dir ,' > $RPM_BUILD_DIR/file.list.kcachegrind
|
||||||
|
find . -type f | sed 's,^\.,\%attr(-\,root\,root) ,' >> $RPM_BUILD_DIR/file.list.kcachegrind
|
||||||
|
find . -type l | sed 's,^\.,\%attr(-\,root\,root) ,' >> $RPM_BUILD_DIR/file.list.kcachegrind
|
||||||
|
|
||||||
|
%clean
|
||||||
|
rm -rf $RPM_BUILD_ROOT/*
|
||||||
|
rm -rf $RPM_BUILD_DIR/kcachegrind
|
||||||
|
rm -rf ../file.list.kcachegrind
|
||||||
|
|
||||||
|
|
||||||
|
%files -f ../file.list.kcachegrind
|
33
kcachegrind/kcachegrind/CMakeLists.txt
Normal file
33
kcachegrind/kcachegrind/CMakeLists.txt
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
include_directories( ../libcore ../libviews )
|
||||||
|
|
||||||
|
########### next target ###############
|
||||||
|
|
||||||
|
set(kcachegrind_SRCS
|
||||||
|
main.cpp
|
||||||
|
kdeconfig.cpp
|
||||||
|
toplevel.cpp
|
||||||
|
configdlg.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
kde4_add_ui_files(kcachegrind_SRCS
|
||||||
|
configdlgbase.ui
|
||||||
|
)
|
||||||
|
|
||||||
|
kde4_add_app_icon(kcachegrind_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/hi*-app-kcachegrind.png")
|
||||||
|
|
||||||
|
kde4_add_executable(kcachegrind ${kcachegrind_SRCS})
|
||||||
|
|
||||||
|
target_link_libraries(kcachegrind core views ${KDE4_KIO_LIBS})
|
||||||
|
|
||||||
|
install(TARGETS kcachegrind ${INSTALL_TARGETS_DEFAULT_ARGS} )
|
||||||
|
|
||||||
|
|
||||||
|
########### install files ###############
|
||||||
|
|
||||||
|
install( PROGRAMS kcachegrind.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} )
|
||||||
|
install( FILES tips DESTINATION ${DATA_INSTALL_DIR}/kcachegrind )
|
||||||
|
install( FILES kcachegrindui.rc DESTINATION ${DATA_INSTALL_DIR}/kcachegrind )
|
||||||
|
|
||||||
|
kde4_install_icons( ${ICON_INSTALL_DIR} )
|
||||||
|
|
||||||
|
|
155
kcachegrind/kcachegrind/Doxyfile
Normal file
155
kcachegrind/kcachegrind/Doxyfile
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
# Doxygen configuration generated by Doxywizard version 0.1
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# General configuration options
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
PROJECT_NAME = kcachegrind
|
||||||
|
PROJECT_NUMBER =
|
||||||
|
OUTPUT_DIRECTORY =
|
||||||
|
OUTPUT_LANGUAGE = English
|
||||||
|
EXTRACT_ALL = YES
|
||||||
|
EXTRACT_PRIVATE = YES
|
||||||
|
EXTRACT_STATIC = YES
|
||||||
|
HIDE_UNDOC_MEMBERS =
|
||||||
|
HIDE_UNDOC_CLASSES =
|
||||||
|
BRIEF_MEMBER_DESC =
|
||||||
|
REPEAT_BRIEF =
|
||||||
|
ALWAYS_DETAILED_SEC =
|
||||||
|
FULL_PATH_NAMES =
|
||||||
|
STRIP_FROM_PATH =
|
||||||
|
INTERNAL_DOCS =
|
||||||
|
CLASS_DIAGRAMS =
|
||||||
|
SOURCE_BROWSER =
|
||||||
|
INLINE_SOURCES =
|
||||||
|
STRIP_CODE_COMMENTS =
|
||||||
|
CASE_SENSE_NAMES =
|
||||||
|
SHORT_NAMES =
|
||||||
|
HIDE_SCOPE_NAMES =
|
||||||
|
VERBATIM_HEADERS =
|
||||||
|
SHOW_INCLUDE_FILES =
|
||||||
|
JAVADOC_AUTOBRIEF =
|
||||||
|
INHERIT_DOCS =
|
||||||
|
INLINE_INFO =
|
||||||
|
SORT_MEMBER_DOCS =
|
||||||
|
DISTRIBUTE_GROUP_DOC =
|
||||||
|
TAB_SIZE =
|
||||||
|
ENABLED_SECTIONS =
|
||||||
|
GENERATE_TODOLIST =
|
||||||
|
GENERATE_TESTLIST =
|
||||||
|
GENERATE_BUGLIST =
|
||||||
|
ALIASES =
|
||||||
|
MAX_INITIALIZER_LINES =
|
||||||
|
OPTIMIZE_OUTPUT_FOR_C =
|
||||||
|
SHOW_USED_FILES =
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# configuration options related to warning and progress messages
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
QUIET =
|
||||||
|
WARNINGS =
|
||||||
|
WARN_IF_UNDOCUMENTED =
|
||||||
|
WARN_FORMAT = "$file:$line: $text"
|
||||||
|
WARN_LOGFILE =
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# configuration options related to the input files
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
INPUT = .
|
||||||
|
FILE_PATTERNS = *.cpp \
|
||||||
|
*.h
|
||||||
|
RECURSIVE = no
|
||||||
|
EXCLUDE =
|
||||||
|
EXCLUDE_PATTERNS =
|
||||||
|
EXAMPLE_PATH =
|
||||||
|
EXAMPLE_PATTERNS =
|
||||||
|
IMAGE_PATH =
|
||||||
|
INPUT_FILTER =
|
||||||
|
FILTER_SOURCE_FILES =
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# configuration options related to the alphabetical class index
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
ALPHABETICAL_INDEX =
|
||||||
|
COLS_IN_ALPHA_INDEX =
|
||||||
|
IGNORE_PREFIX =
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# configuration options related to the HTML output
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
GENERATE_HTML =
|
||||||
|
HTML_OUTPUT = html
|
||||||
|
HTML_HEADER =
|
||||||
|
HTML_FOOTER =
|
||||||
|
HTML_STYLESHEET =
|
||||||
|
HTML_ALIGN_MEMBERS =
|
||||||
|
GENERATE_HTMLHELP =
|
||||||
|
GENERATE_CHI =
|
||||||
|
BINARY_TOC =
|
||||||
|
TOC_EXPAND =
|
||||||
|
DISABLE_INDEX =
|
||||||
|
ENUM_VALUES_PER_LINE =
|
||||||
|
GENERATE_TREEVIEW =
|
||||||
|
TREEVIEW_WIDTH =
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# configuration options related to the LaTeX output
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
GENERATE_LATEX = NO
|
||||||
|
LATEX_OUTPUT = latex
|
||||||
|
COMPACT_LATEX =
|
||||||
|
PAPER_TYPE = a4wide
|
||||||
|
EXTRA_PACKAGES =
|
||||||
|
LATEX_HEADER =
|
||||||
|
PDF_HYPERLINKS =
|
||||||
|
USE_PDFLATEX =
|
||||||
|
LATEX_BATCHMODE =
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# configuration options related to the RTF output
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
GENERATE_RTF = NO
|
||||||
|
RTF_OUTPUT = rtf
|
||||||
|
COMPACT_RTF =
|
||||||
|
RTF_HYPERLINKS =
|
||||||
|
RTF_STYLESHEET_FILE =
|
||||||
|
RTF_EXTENSIONS_FILE =
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# configuration options related to the man page output
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
GENERATE_MAN = NO
|
||||||
|
MAN_OUTPUT = man
|
||||||
|
MAN_EXTENSION = .3
|
||||||
|
MAN_LINKS =
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to the preprocessor
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
ENABLE_PREPROCESSING =
|
||||||
|
MACRO_EXPANSION =
|
||||||
|
EXPAND_ONLY_PREDEF =
|
||||||
|
SEARCH_INCLUDES =
|
||||||
|
INCLUDE_PATH =
|
||||||
|
INCLUDE_FILE_PATTERNS =
|
||||||
|
PREDEFINED =
|
||||||
|
EXPAND_AS_DEFINED =
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration::addtions related to external references
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
TAGFILES =
|
||||||
|
GENERATE_TAGFILE =
|
||||||
|
ALLEXTERNALS =
|
||||||
|
PERL_PATH = /usr/bin/perl
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to the dot tool
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
HAVE_DOT =
|
||||||
|
CLASS_GRAPH =
|
||||||
|
COLLABORATION_GRAPH =
|
||||||
|
INCLUDE_GRAPH =
|
||||||
|
INCLUDED_BY_GRAPH =
|
||||||
|
GRAPHICAL_HIERARCHY =
|
||||||
|
DOT_PATH =
|
||||||
|
GENERATE_LEGEND =
|
||||||
|
DOT_CLEANUP =
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration::addtions related to the search engine
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
SEARCHENGINE =
|
||||||
|
CGI_NAME = search.cgi
|
||||||
|
CGI_URL =
|
||||||
|
DOC_URL =
|
||||||
|
DOC_ABSPATH =
|
||||||
|
BIN_ABSPATH = /usr/local/bin/
|
||||||
|
EXT_DOC_PATHS =
|
375
kcachegrind/kcachegrind/configdlg.cpp
Normal file
375
kcachegrind/kcachegrind/configdlg.cpp
Normal file
|
@ -0,0 +1,375 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002, 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configuration Dialog for KCachegrind
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "configdlg.h"
|
||||||
|
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include <kcolorbutton.h>
|
||||||
|
#include <kfiledialog.h>
|
||||||
|
#include <klocale.h>
|
||||||
|
#include <knumvalidator.h>
|
||||||
|
|
||||||
|
#include "tracedata.h"
|
||||||
|
#include "globalguiconfig.h"
|
||||||
|
|
||||||
|
|
||||||
|
ConfigDlg::ConfigDlg(GlobalGUIConfig* c, TraceData* data,
|
||||||
|
QWidget* parent)
|
||||||
|
:ConfigDlgBase(parent)
|
||||||
|
{
|
||||||
|
_config = c;
|
||||||
|
_data = data;
|
||||||
|
_objectCS = 0;
|
||||||
|
_classCS = 0;
|
||||||
|
_fileCS = 0;
|
||||||
|
|
||||||
|
connect(objectCombo, SIGNAL(activated(const QString &)),
|
||||||
|
this, SLOT(objectActivated(const QString &)));
|
||||||
|
connect(objectCombo, SIGNAL(textChanged(const QString &)),
|
||||||
|
this, SLOT(objectActivated(const QString &)));
|
||||||
|
connect(objectCheck, SIGNAL(toggled(bool)),
|
||||||
|
this, SLOT(objectCheckChanged(bool)));
|
||||||
|
connect(objectColor, SIGNAL(changed(const QColor &)),
|
||||||
|
this, SLOT(objectColorChanged(const QColor &)));
|
||||||
|
|
||||||
|
connect(classCombo, SIGNAL(activated(const QString &)),
|
||||||
|
this, SLOT(classActivated(const QString &)));
|
||||||
|
connect(classCombo, SIGNAL(textChanged(const QString &)),
|
||||||
|
this, SLOT(classActivated(const QString &)));
|
||||||
|
connect(classCheck, SIGNAL(toggled(bool)),
|
||||||
|
this, SLOT(classCheckChanged(bool)));
|
||||||
|
connect(classColor, SIGNAL(changed(const QColor &)),
|
||||||
|
this, SLOT(classColorChanged(const QColor &)));
|
||||||
|
|
||||||
|
connect(fileCombo, SIGNAL(activated(const QString &)),
|
||||||
|
this, SLOT(fileActivated(const QString &)));
|
||||||
|
connect(fileCombo, SIGNAL(textChanged(const QString &)),
|
||||||
|
this, SLOT(fileActivated(const QString &)));
|
||||||
|
connect(fileCheck, SIGNAL(toggled(bool)),
|
||||||
|
this, SLOT(fileCheckChanged(bool)));
|
||||||
|
connect(fileColor, SIGNAL(changed(const QColor &)),
|
||||||
|
this, SLOT(fileColorChanged(const QColor &)));
|
||||||
|
|
||||||
|
connect(buttonBox, SIGNAL(accepted()),SLOT(accept()));
|
||||||
|
connect(buttonBox, SIGNAL(rejected()),SLOT(reject()));
|
||||||
|
QString objectPrefix = ProfileContext::typeName(ProfileContext::Object);
|
||||||
|
QString classPrefix = ProfileContext::typeName(ProfileContext::Class);
|
||||||
|
QString filePrefix = ProfileContext::typeName(ProfileContext::File);
|
||||||
|
|
||||||
|
objectCombo->setDuplicatesEnabled(false);
|
||||||
|
classCombo->setDuplicatesEnabled(false);
|
||||||
|
fileCombo->setDuplicatesEnabled(false);
|
||||||
|
objectCombo->setAutoCompletion(true);
|
||||||
|
classCombo->setAutoCompletion(true);
|
||||||
|
fileCombo->setAutoCompletion(true);
|
||||||
|
|
||||||
|
// first unspecified cost items from data
|
||||||
|
TraceObjectMap::Iterator oit;
|
||||||
|
QStringList oList;
|
||||||
|
if (data) {
|
||||||
|
for ( oit = data->objectMap().begin();
|
||||||
|
oit != data->objectMap().end(); ++oit )
|
||||||
|
oList.append((*oit).prettyName());
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceClassMap::Iterator cit;
|
||||||
|
QStringList cList;
|
||||||
|
if (data) {
|
||||||
|
for ( cit = data->classMap().begin();
|
||||||
|
cit != data->classMap().end(); ++cit )
|
||||||
|
cList.append((*cit).prettyName());
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceFileMap::Iterator fit;
|
||||||
|
QStringList fList;
|
||||||
|
if (data) {
|
||||||
|
for ( fit = data->fileMap().begin();
|
||||||
|
fit != data->fileMap().end(); ++fit )
|
||||||
|
fList.append((*fit).prettyName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// then already defined colors (have to check for duplicates!)
|
||||||
|
foreach(ConfigColorSetting* cs, c->_colors) {
|
||||||
|
if (cs->_automatic) continue;
|
||||||
|
|
||||||
|
QString n = cs->_name;
|
||||||
|
if (n.startsWith(objectPrefix)) {
|
||||||
|
n = n.remove(0, objectPrefix.length()+1);
|
||||||
|
if (oList.indexOf(n) == -1) oList.append(n);
|
||||||
|
}
|
||||||
|
else if (n.startsWith(classPrefix)) {
|
||||||
|
n = n.remove(0, classPrefix.length()+1);
|
||||||
|
if (cList.indexOf(n) == -1) cList.append(n);
|
||||||
|
}
|
||||||
|
else if (n.startsWith(filePrefix)) {
|
||||||
|
n = n.remove(0, filePrefix.length()+1);
|
||||||
|
if (fList.indexOf(n) == -1) fList.append(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
oList.sort();
|
||||||
|
cList.sort();
|
||||||
|
fList.sort();
|
||||||
|
objectCombo->addItems(oList);
|
||||||
|
classCombo->addItems(cList);
|
||||||
|
fileCombo->addItems(fList);
|
||||||
|
|
||||||
|
objectActivated(objectCombo->currentText());
|
||||||
|
classActivated(classCombo->currentText());
|
||||||
|
fileActivated(fileCombo->currentText());
|
||||||
|
|
||||||
|
maxListEdit->setValue(c->_maxListCount);
|
||||||
|
|
||||||
|
QTreeWidgetItem* i = new QTreeWidgetItem(dirList);
|
||||||
|
i->setText(0, i18n("(always)"));
|
||||||
|
i->setExpanded(true);
|
||||||
|
QTreeWidgetItem* root = i;
|
||||||
|
QStringList::const_iterator sit = c->_generalSourceDirs.constBegin();
|
||||||
|
for(; sit != c->_generalSourceDirs.constEnd(); ++sit ) {
|
||||||
|
QTreeWidgetItem *item = new QTreeWidgetItem(i);
|
||||||
|
item->setText(0, sit->isEmpty() ? QDir::rootPath() : *sit);
|
||||||
|
}
|
||||||
|
if (data) {
|
||||||
|
for ( oit = data->objectMap().begin();
|
||||||
|
oit != data->objectMap().end(); ++oit ) {
|
||||||
|
const QString n = (*oit).name();
|
||||||
|
if (n.isEmpty()) continue;
|
||||||
|
i = new QTreeWidgetItem(dirList);
|
||||||
|
i->setText(0, n);
|
||||||
|
i->setExpanded(true);
|
||||||
|
const QStringList dirs = c->_objectSourceDirs[n];
|
||||||
|
|
||||||
|
sit = dirs.constBegin();
|
||||||
|
for(; sit != dirs.constEnd(); ++sit ) {
|
||||||
|
QTreeWidgetItem *item = new QTreeWidgetItem(i);
|
||||||
|
item->setText(0, sit->isEmpty() ? QDir::rootPath() : *sit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(dirList, SIGNAL(itemSelectionChanged()),
|
||||||
|
this, SLOT(dirsItemChanged()));
|
||||||
|
connect(addDirButton, SIGNAL(clicked()),
|
||||||
|
this, SLOT(dirsAddPressed()));
|
||||||
|
connect(deleteDirButton, SIGNAL(clicked()),
|
||||||
|
this, SLOT(dirsDeletePressed()));
|
||||||
|
dirList->setCurrentItem(root);
|
||||||
|
|
||||||
|
symbolCount->setValue(c->_maxSymbolCount);
|
||||||
|
symbolLength->setValue(c->_maxSymbolLength);
|
||||||
|
precisionEdit->setValue(c->_percentPrecision);
|
||||||
|
contextEdit->setValue(c->_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigDlg::~ConfigDlg()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigDlg::configure(GlobalGUIConfig* c, TraceData* d, QWidget* p)
|
||||||
|
{
|
||||||
|
ConfigDlg dlg(c, d, p);
|
||||||
|
|
||||||
|
if (dlg.exec()) {
|
||||||
|
|
||||||
|
// max 499 per definition
|
||||||
|
c->_maxListCount = dlg.maxListEdit->value();
|
||||||
|
c->_maxSymbolCount = dlg.symbolCount->value();
|
||||||
|
c->_maxSymbolLength = dlg.symbolLength->value();
|
||||||
|
c->_percentPrecision = dlg.precisionEdit->value();
|
||||||
|
c->_context = dlg.contextEdit->value();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigDlg::objectActivated(const QString & s)
|
||||||
|
{
|
||||||
|
// qDebug("objectActivated: %s", s.ascii());
|
||||||
|
|
||||||
|
if (s.isEmpty()) { _objectCS=0; return; }
|
||||||
|
|
||||||
|
QString n = ProfileContext::typeName(ProfileContext::Object) + '-' + s;
|
||||||
|
|
||||||
|
ConfigColorSetting* cs = _config->_colors[n];
|
||||||
|
if (!cs)
|
||||||
|
cs = GlobalGUIConfig::colorSetting(n);
|
||||||
|
// else
|
||||||
|
// qDebug("found color %s", n.ascii());
|
||||||
|
|
||||||
|
_objectCS = cs;
|
||||||
|
|
||||||
|
objectCheck->setChecked(cs->_automatic);
|
||||||
|
objectColor->setColor(cs->_color);
|
||||||
|
|
||||||
|
/*
|
||||||
|
qDebug("Found Color %s, automatic to %s",
|
||||||
|
_objectCS->name.ascii(),
|
||||||
|
_objectCS->automatic ? "true":"false");
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ConfigDlg::objectCheckChanged(bool b)
|
||||||
|
{
|
||||||
|
if (_objectCS) {
|
||||||
|
_objectCS->_automatic = b;
|
||||||
|
/*
|
||||||
|
qDebug("Set Color %s automatic to %s",
|
||||||
|
_objectCS->name.ascii(),
|
||||||
|
_objectCS->automatic ? "true":"false");
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigDlg::objectColorChanged(const QColor & c)
|
||||||
|
{
|
||||||
|
if (_objectCS) _objectCS->_color = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigDlg::classActivated(const QString & s)
|
||||||
|
{
|
||||||
|
// qDebug("classActivated: %s", s.ascii());
|
||||||
|
|
||||||
|
if (s.isEmpty()) { _classCS=0; return; }
|
||||||
|
|
||||||
|
QString n = ProfileContext::typeName(ProfileContext::Class) + '-' + s;
|
||||||
|
|
||||||
|
ConfigColorSetting* cs = _config->_colors[n];
|
||||||
|
if (!cs)
|
||||||
|
cs = GlobalGUIConfig::colorSetting(n);
|
||||||
|
|
||||||
|
_classCS = cs;
|
||||||
|
|
||||||
|
classCheck->setChecked(cs->_automatic);
|
||||||
|
classColor->setColor(cs->_color);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ConfigDlg::classCheckChanged(bool b)
|
||||||
|
{
|
||||||
|
if (_classCS) _classCS->_automatic = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigDlg::classColorChanged(const QColor & c)
|
||||||
|
{
|
||||||
|
if (_classCS) _classCS->_color = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ConfigDlg::fileActivated(const QString & s)
|
||||||
|
{
|
||||||
|
// qDebug("fileActivated: %s", s.ascii());
|
||||||
|
|
||||||
|
if (s.isEmpty()) { _fileCS=0; return; }
|
||||||
|
|
||||||
|
QString n = ProfileContext::typeName(ProfileContext::File) + '-' + s;
|
||||||
|
|
||||||
|
ConfigColorSetting* cs = _config->_colors[n];
|
||||||
|
if (!cs)
|
||||||
|
cs = GlobalGUIConfig::colorSetting(n);
|
||||||
|
|
||||||
|
_fileCS = cs;
|
||||||
|
|
||||||
|
fileCheck->setChecked(cs->_automatic);
|
||||||
|
fileColor->setColor(cs->_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ConfigDlg::fileCheckChanged(bool b)
|
||||||
|
{
|
||||||
|
if (_fileCS) _fileCS->_automatic = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigDlg::fileColorChanged(const QColor & c)
|
||||||
|
{
|
||||||
|
if (_fileCS) _fileCS->_color = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTreeWidgetItem *ConfigDlg::getSelectedDirItem()
|
||||||
|
{
|
||||||
|
const QList<QTreeWidgetItem*> selectedItems = dirList->selectedItems();
|
||||||
|
return selectedItems.count() ? selectedItems[0] : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigDlg::dirsItemChanged()
|
||||||
|
{
|
||||||
|
QTreeWidgetItem *dirItem = getSelectedDirItem();
|
||||||
|
deleteDirButton->setEnabled(dirItem && dirItem->parent() != NULL);
|
||||||
|
addDirButton->setEnabled(dirItem && dirItem->parent() == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigDlg::dirsDeletePressed()
|
||||||
|
{
|
||||||
|
QTreeWidgetItem *dirItem = getSelectedDirItem();
|
||||||
|
if (!dirItem || (dirItem->parent() == 0)) return;
|
||||||
|
QTreeWidgetItem* p = dirItem->parent();
|
||||||
|
if (!p) return;
|
||||||
|
|
||||||
|
QString objName = p->text(0);
|
||||||
|
|
||||||
|
QStringList* dirs;
|
||||||
|
if (objName == i18n("(always)"))
|
||||||
|
dirs = &(_config->_generalSourceDirs);
|
||||||
|
else
|
||||||
|
dirs = &(_config->_objectSourceDirs[objName]);
|
||||||
|
|
||||||
|
dirs->removeAll(dirItem->text(0));
|
||||||
|
delete dirItem;
|
||||||
|
|
||||||
|
deleteDirButton->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigDlg::dirsAddPressed()
|
||||||
|
{
|
||||||
|
QTreeWidgetItem *dirItem = getSelectedDirItem();
|
||||||
|
if (!dirItem || (dirItem->parent() != 0)) return;
|
||||||
|
|
||||||
|
QString objName = dirItem->text(0);
|
||||||
|
|
||||||
|
QStringList* dirs;
|
||||||
|
if (objName == i18n("(always)"))
|
||||||
|
dirs = &(_config->_generalSourceDirs);
|
||||||
|
else
|
||||||
|
dirs = &(_config->_objectSourceDirs[objName]);
|
||||||
|
|
||||||
|
QString newDir;
|
||||||
|
newDir = KFileDialog::getExistingDirectory(KUrl(),
|
||||||
|
this,
|
||||||
|
i18n("Choose Source Folder"));
|
||||||
|
if (newDir.isEmpty()) return;
|
||||||
|
|
||||||
|
// even for '/', we strip the tailing slash
|
||||||
|
if (newDir.endsWith(QLatin1Char('/')))
|
||||||
|
newDir = newDir.left(newDir.length()-1);
|
||||||
|
|
||||||
|
if (dirs->indexOf(newDir)>=0) return;
|
||||||
|
|
||||||
|
dirs->append(newDir);
|
||||||
|
if (newDir.isEmpty()) newDir = QDir::rootPath();
|
||||||
|
QTreeWidgetItem *item = new QTreeWidgetItem(dirItem);
|
||||||
|
item->setText(0, newDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "moc_configdlg.cpp"
|
73
kcachegrind/kcachegrind/configdlg.h
Normal file
73
kcachegrind/kcachegrind/configdlg.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002, 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configuration Dialog for KCachegrind
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONFIGDLG_H
|
||||||
|
#define CONFIGDLG_H
|
||||||
|
|
||||||
|
#include "ui_configdlgbase.h"
|
||||||
|
|
||||||
|
class TraceData;
|
||||||
|
class GlobalGUIConfig;
|
||||||
|
class ConfigColorSetting;
|
||||||
|
|
||||||
|
class ConfigDlgBase : public QDialog, public Ui::ConfigDlgBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConfigDlgBase( QWidget *parent ) : QDialog( parent ) {
|
||||||
|
setupUi( this );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConfigDlg : public ConfigDlgBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ConfigDlg(GlobalGUIConfig*, TraceData*,
|
||||||
|
QWidget* parent = 0);
|
||||||
|
~ConfigDlg();
|
||||||
|
|
||||||
|
static bool configure(GlobalGUIConfig*, TraceData*, QWidget*);
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void objectActivated(const QString &);
|
||||||
|
void objectCheckChanged(bool);
|
||||||
|
void objectColorChanged(const QColor &);
|
||||||
|
void classActivated(const QString &);
|
||||||
|
void classCheckChanged(bool);
|
||||||
|
void classColorChanged(const QColor &);
|
||||||
|
void fileActivated(const QString &);
|
||||||
|
void fileCheckChanged(bool);
|
||||||
|
void fileColorChanged(const QColor &);
|
||||||
|
void dirsItemChanged();
|
||||||
|
void dirsDeletePressed();
|
||||||
|
void dirsAddPressed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTreeWidgetItem *getSelectedDirItem();
|
||||||
|
GlobalGUIConfig* _config;
|
||||||
|
TraceData* _data;
|
||||||
|
|
||||||
|
ConfigColorSetting *_objectCS, *_classCS, *_fileCS;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
519
kcachegrind/kcachegrind/configdlgbase.ui
Normal file
519
kcachegrind/kcachegrind/configdlgbase.ui
Normal file
|
@ -0,0 +1,519 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ConfigDlgBase</class>
|
||||||
|
<widget class="QDialog" name="ConfigDlgBase">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>394</width>
|
||||||
|
<height>328</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Configuration</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTabWidget" name="tabWidget2">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tab1">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>General</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="TextLabel5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Maximum number of items in lists:</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="4">
|
||||||
|
<widget class="QLabel" name="TextLabel3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Truncate symbols in tooltips and context menus</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<spacer name="Spacer6_2_2_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>16</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLabel" name="TextLabel2">
|
||||||
|
<property name="text">
|
||||||
|
<string>when more than:</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLabel" name="TextLabel2_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>when longer than:</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="TextLabel4_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Precision of percentage values:</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="3">
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="2">
|
||||||
|
<widget class="KIntSpinBox" name="precisionEdit">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<widget class="KIntSpinBox" name="symbolLength">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>999</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="KIntSpinBox" name="symbolCount">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>999</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="KIntSpinBox" name="maxListEdit">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>The Maximum Number of List Items should be below 500.</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>499</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="TextLabel1">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::NoFrame</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Plain</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Cost Item Colors</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QCheckBox" name="fileCheck">
|
||||||
|
<property name="text">
|
||||||
|
<string>Automatic</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="TextLabel4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Object:</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="TextLabel4_2_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Class:</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="3">
|
||||||
|
<widget class="KColorButton" name="fileColor">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QCheckBox" name="classCheck">
|
||||||
|
<property name="text">
|
||||||
|
<string>Automatic</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="3">
|
||||||
|
<widget class="KColorButton" name="objectColor">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QCheckBox" name="objectCheck">
|
||||||
|
<property name="text">
|
||||||
|
<string>Automatic</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="TextLabel4_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>File:</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="3">
|
||||||
|
<widget class="KColorButton" name="classColor">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="4">
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::MinimumExpanding</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="KComboBox" name="objectCombo">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>150</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="editable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="KComboBox" name="classCombo">
|
||||||
|
<property name="editable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="KComboBox" name="fileCombo">
|
||||||
|
<property name="editable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::MinimumExpanding</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab2">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Annotations</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="TextLabel4_3_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Context lines in annotations:</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="KIntSpinBox" name="contextEdit"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="TextLabel1_2">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Source Folders</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<layout class="QVBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="KPushButton" name="addDirButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Add</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="KPushButton" name="deleteDirButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Delete</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="Spacer5">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Expanding</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>16</width>
|
||||||
|
<height>49</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QTreeWidget" name="dirList">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<property name="showDropIndicator" stdset="0">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Object / Related Source Base</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="KDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>KIntSpinBox</class>
|
||||||
|
<extends>QSpinBox</extends>
|
||||||
|
<header>knuminput.h</header>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>KDialogButtonBox</class>
|
||||||
|
<extends>QDialogButtonBox</extends>
|
||||||
|
<header>kdialogbuttonbox.h</header>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>KPushButton</class>
|
||||||
|
<extends>QPushButton</extends>
|
||||||
|
<header>kpushbutton.h</header>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>KComboBox</class>
|
||||||
|
<extends>QComboBox</extends>
|
||||||
|
<header>kcombobox.h</header>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>KColorButton</class>
|
||||||
|
<extends>QPushButton</extends>
|
||||||
|
<header>kcolorbutton.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>tabWidget2</tabstop>
|
||||||
|
<tabstop>objectCombo</tabstop>
|
||||||
|
<tabstop>objectCheck</tabstop>
|
||||||
|
<tabstop>objectColor</tabstop>
|
||||||
|
<tabstop>classCombo</tabstop>
|
||||||
|
<tabstop>classCheck</tabstop>
|
||||||
|
<tabstop>classColor</tabstop>
|
||||||
|
<tabstop>fileCombo</tabstop>
|
||||||
|
<tabstop>fileCheck</tabstop>
|
||||||
|
<tabstop>fileColor</tabstop>
|
||||||
|
<tabstop>buttonBox</tabstop>
|
||||||
|
<tabstop>dirList</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>classCheck</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>classColor</receiver>
|
||||||
|
<slot>setDisabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>20</x>
|
||||||
|
<y>20</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>20</x>
|
||||||
|
<y>20</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>fileCheck</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>fileColor</receiver>
|
||||||
|
<slot>setDisabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>20</x>
|
||||||
|
<y>20</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>20</x>
|
||||||
|
<y>20</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>objectCheck</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>objectColor</receiver>
|
||||||
|
<slot>setDisabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>20</x>
|
||||||
|
<y>20</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>20</x>
|
||||||
|
<y>20</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
66
kcachegrind/kcachegrind/dumpmanager.cpp
Normal file
66
kcachegrind/kcachegrind/dumpmanager.cpp
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DumpManager
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dumpmanager.h"
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Dump
|
||||||
|
//
|
||||||
|
|
||||||
|
Dump::Dump(QString file)
|
||||||
|
{
|
||||||
|
_filename = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// DumpManager
|
||||||
|
//
|
||||||
|
|
||||||
|
DumpManager* DumpManager::_self = 0;
|
||||||
|
|
||||||
|
|
||||||
|
DumpManager::DumpManager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DumpManager* DumpManager::self()
|
||||||
|
{
|
||||||
|
if (!_self)
|
||||||
|
_self = new DumpManager();
|
||||||
|
|
||||||
|
return _self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DumpList DumpManager::loadableDumps()
|
||||||
|
{
|
||||||
|
DumpList res;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceData* DumpManager::load(Dump*)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
75
kcachegrind/kcachegrind/dumpmanager.h
Normal file
75
kcachegrind/kcachegrind/dumpmanager.h
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DumpManager
|
||||||
|
*
|
||||||
|
* DumpManager is a Singleton.
|
||||||
|
* - Has List of current loaded dumps / loadable dumps
|
||||||
|
* - Does "communication" with current running profiles
|
||||||
|
* for dump selection dockable
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DUMPMANAGER_H
|
||||||
|
#define DUMPMANAGER_H
|
||||||
|
|
||||||
|
#include <qstring.h>
|
||||||
|
#include <qlist.h>
|
||||||
|
|
||||||
|
class Dump;
|
||||||
|
class TraceData;
|
||||||
|
|
||||||
|
typedef QList<Dump*> DumpList;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A loadable profile Dump
|
||||||
|
*/
|
||||||
|
class Dump
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Dump(QString);
|
||||||
|
|
||||||
|
QString filename() const { return _filename; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO:
|
||||||
|
* - Everything
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DumpManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DumpManager();
|
||||||
|
|
||||||
|
DumpManager* self();
|
||||||
|
|
||||||
|
DumpList loadableDumps();
|
||||||
|
TraceData* load(Dump*);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static DumpManager* _self;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
49
kcachegrind/kcachegrind/dumpselection.cpp
Normal file
49
kcachegrind/kcachegrind/dumpselection.cpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DumpSelection Dockable
|
||||||
|
*
|
||||||
|
* - Fast Selection of dumps to load/activate/use for comparing
|
||||||
|
* - Start a profile run from GUI (current supported: Callgrind)
|
||||||
|
* - View state of running profile runs.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dumpselection.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO:
|
||||||
|
* - Everything !!
|
||||||
|
* - Request State info on current function..
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
DumpSelection::DumpSelection( TopLevel* top,
|
||||||
|
QWidget* parent, const char* name)
|
||||||
|
: DumpSelectionBase(parent, name), TraceItemView(0, top)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DumpSelection::~DumpSelection()
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
#include "moc_dumpselection.cpp"
|
||||||
|
|
45
kcachegrind/kcachegrind/dumpselection.h
Normal file
45
kcachegrind/kcachegrind/dumpselection.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DumpSelection Dockable
|
||||||
|
*
|
||||||
|
* - Fast Selection of dumps to load/activate/use for comparing
|
||||||
|
* - Start a profile run from GUI (current supported: Callgrind)
|
||||||
|
* - View state of running profile runs.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DUMPSELECTION_H
|
||||||
|
#define DUMPSELECTION_H
|
||||||
|
|
||||||
|
#include "dumpselectionbase.h"
|
||||||
|
#include "traceitemview.h"
|
||||||
|
|
||||||
|
class DumpSelection : public DumpSelectionBase, public TraceItemView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DumpSelection( TopLevel*, QWidget* parent = 0, const char* name = 0);
|
||||||
|
virtual ~DumpSelection();
|
||||||
|
|
||||||
|
QWidget* widget() { return this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
1021
kcachegrind/kcachegrind/dumpselectionbase.ui
Normal file
1021
kcachegrind/kcachegrind/dumpselectionbase.ui
Normal file
File diff suppressed because it is too large
Load diff
BIN
kcachegrind/kcachegrind/hi32-app-kcachegrind.png
Normal file
BIN
kcachegrind/kcachegrind/hi32-app-kcachegrind.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
kcachegrind/kcachegrind/hi48-app-kcachegrind.png
Normal file
BIN
kcachegrind/kcachegrind/hi48-app-kcachegrind.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
178
kcachegrind/kcachegrind/kcachegrind.desktop
Executable file
178
kcachegrind/kcachegrind/kcachegrind.desktop
Executable file
|
@ -0,0 +1,178 @@
|
||||||
|
# KDE Config File
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Exec=kcachegrind -caption %c %u
|
||||||
|
MimeType=application/x-kcachegrind;
|
||||||
|
Icon=kcachegrind
|
||||||
|
X-DocPath=kcachegrind/index.html
|
||||||
|
Terminal=false
|
||||||
|
Name=KCachegrind
|
||||||
|
Name[ast]=KCachegrind
|
||||||
|
Name[bg]=KCachegrind
|
||||||
|
Name[br]=KCachegrind
|
||||||
|
Name[bs]=KCachegrind
|
||||||
|
Name[ca]=KCachegrind
|
||||||
|
Name[ca@valencia]=KCachegrind
|
||||||
|
Name[cs]=KCachegrind
|
||||||
|
Name[cy]=KCachegrind
|
||||||
|
Name[da]=KCachegrind
|
||||||
|
Name[de]=KCachegrind
|
||||||
|
Name[el]=KCachegrind
|
||||||
|
Name[en_GB]=KCachegrind
|
||||||
|
Name[eo]=KCachegrind
|
||||||
|
Name[es]=KCachegrind
|
||||||
|
Name[et]=KCachegrind
|
||||||
|
Name[eu]=KCachegrind
|
||||||
|
Name[fi]=KCachegrind
|
||||||
|
Name[fr]=KCachegrind
|
||||||
|
Name[ga]=KCachegrind
|
||||||
|
Name[gl]=KCachegrind
|
||||||
|
Name[he]=KCachegrind
|
||||||
|
Name[hu]=KCachegrind
|
||||||
|
Name[is]=KCachegrind
|
||||||
|
Name[it]=KCachegrind
|
||||||
|
Name[ja]=KCachegrind
|
||||||
|
Name[kk]=KCachegrind
|
||||||
|
Name[km]=KCachegrind
|
||||||
|
Name[ko]=KCachegrind
|
||||||
|
Name[lt]=KCachegrind
|
||||||
|
Name[lv]=KCachegrind
|
||||||
|
Name[mr]=के-कॅशेग्रिन्ड
|
||||||
|
Name[nb]=KCachegrind
|
||||||
|
Name[nds]=KCachegrind
|
||||||
|
Name[ne]=केडीई क्यास ग्रिन्ड
|
||||||
|
Name[nl]=KCachegrind
|
||||||
|
Name[nn]=KCachegrind
|
||||||
|
Name[pa]=KCachegrind
|
||||||
|
Name[pl]=KCachegrind
|
||||||
|
Name[pt]=KCachegrind
|
||||||
|
Name[pt_BR]=KCachegrind
|
||||||
|
Name[ro]=KCachegrind
|
||||||
|
Name[ru]=KCachegrind
|
||||||
|
Name[sk]=KCachegrind
|
||||||
|
Name[sl]=KCachegrind
|
||||||
|
Name[sr]=К‑кешгринд
|
||||||
|
Name[sr@ijekavian]=К‑кешгринд
|
||||||
|
Name[sr@ijekavianlatin]=KCacheGrind
|
||||||
|
Name[sr@latin]=KCacheGrind
|
||||||
|
Name[sv]=Kcachegrind
|
||||||
|
Name[ta]= இடைமாற்றகட்டம்
|
||||||
|
Name[tg]=KCachegrind
|
||||||
|
Name[tr]=KCachegrind
|
||||||
|
Name[ug]=KCachegrind
|
||||||
|
Name[uk]=KCachegrind
|
||||||
|
Name[x-test]=xxKCachegrindxx
|
||||||
|
Name[zh_CN]=KCachegrind
|
||||||
|
Name[zh_TW]=KCachegrind
|
||||||
|
GenericName=Profiler Frontend
|
||||||
|
GenericName[ast]=Interface del analizador de rendimientu
|
||||||
|
GenericName[bs]=Profajlerski program
|
||||||
|
GenericName[ca]=Frontal del Profiler
|
||||||
|
GenericName[ca@valencia]=Frontal del Profiler
|
||||||
|
GenericName[cs]=Rozhraní pro profilaci
|
||||||
|
GenericName[cy]=Blaen-wyneb Proffilydd
|
||||||
|
GenericName[da]=Grænseflade til profilering
|
||||||
|
GenericName[de]=Profiler-Oberfläche
|
||||||
|
GenericName[el]=Πρόγραμμα προφίλ
|
||||||
|
GenericName[en_GB]=Profiler Frontend
|
||||||
|
GenericName[eo]=Fasado de Profililo
|
||||||
|
GenericName[es]=Interfaz del analizador de rendimiento
|
||||||
|
GenericName[et]=Profileerimisrakendus
|
||||||
|
GenericName[eu]=Profilatzailearen interfazea
|
||||||
|
GenericName[fa]=پایانه گزارشگیر
|
||||||
|
GenericName[fi]=Profiloijan käyttöliittymä
|
||||||
|
GenericName[fr]=Interface de profilage
|
||||||
|
GenericName[ga]=Comhéadan ar Phróifíleoir
|
||||||
|
GenericName[gl]=Interface para o profiler
|
||||||
|
GenericName[hu]=Ábrázoló előtétprogram
|
||||||
|
GenericName[is]=Myndrænt viðmót á afkastakönnuð
|
||||||
|
GenericName[it]=Interfaccia a profiler
|
||||||
|
GenericName[ja]=プロファイラフロントエンド
|
||||||
|
GenericName[kk]=Профильдеткіштің интерфейсі
|
||||||
|
GenericName[km]=កម្មវិធីផ្នែកខាងមុខរបស់ទម្រង់
|
||||||
|
GenericName[ko]=프로파일러 프론트엔드
|
||||||
|
GenericName[lt]=Profiliuoklio naudotojo sąsaja
|
||||||
|
GenericName[lv]=Profilēšanas priekšpuse
|
||||||
|
GenericName[mr]=प्रोफाइलर फ्रंटएन्ड
|
||||||
|
GenericName[nb]=Grensesnitt for profilvisning
|
||||||
|
GenericName[nds]=Profiler-Böversiet
|
||||||
|
GenericName[ne]=प्रोफाइलर फ्रन्टइन्ड
|
||||||
|
GenericName[nl]=Profiler-hulpprogramma
|
||||||
|
GenericName[nn]=Grensesnitt for profilvising
|
||||||
|
GenericName[pa]=ਪਰੋਫਾਇਲਰ ਫਰੰਟ-ਐਂਡ
|
||||||
|
GenericName[pl]=Interfejs do profilera
|
||||||
|
GenericName[pt]=Interface de Análise de Performance
|
||||||
|
GenericName[pt_BR]=Interface de análise de performance
|
||||||
|
GenericName[ro]=Interfață grafică pentru Profiler
|
||||||
|
GenericName[ru]=Интерфейс к профилировщику
|
||||||
|
GenericName[sk]=Rozhranie pre profiler
|
||||||
|
GenericName[sl]=Vmesnik profilnika
|
||||||
|
GenericName[sr]=Прочеље профилизатора
|
||||||
|
GenericName[sr@ijekavian]=Прочеље профилизатора
|
||||||
|
GenericName[sr@ijekavianlatin]=Pročelje profilizatora
|
||||||
|
GenericName[sr@latin]=Pročelje profilizatora
|
||||||
|
GenericName[sv]=Profileringsgränssnitt
|
||||||
|
GenericName[ta]= விவரக்குறிப்பு முன்பகுதி
|
||||||
|
GenericName[tg]=Интерфейс ба профилкунанда
|
||||||
|
GenericName[tr]=Profil Önyüzü
|
||||||
|
GenericName[uk]=Інтерфейс до Profiler
|
||||||
|
GenericName[wa]=Eterface grafike po Profiler
|
||||||
|
GenericName[x-test]=xxProfiler Frontendxx
|
||||||
|
GenericName[zh_CN]=性能测试数据前端
|
||||||
|
GenericName[zh_TW]=分析器前端
|
||||||
|
Comment=Visualization of Performance Profiling Data
|
||||||
|
Comment[ast]=Visualización de datos d'analís de rendimientu
|
||||||
|
Comment[bg]=Визуализация на данните за производителност
|
||||||
|
Comment[bs]=Predočenje podataka profiliranja performansi
|
||||||
|
Comment[ca]=Visualització de dades de perfilat de rendiment
|
||||||
|
Comment[ca@valencia]=Visualització de dades de perfilat de rendiment
|
||||||
|
Comment[cs]=Vizualizace profilovacích dat výkonu
|
||||||
|
Comment[da]=Visualisering af profileringsdata
|
||||||
|
Comment[de]=Visualisierung von Daten des Laufzeitverhaltens eines Programmes
|
||||||
|
Comment[el]=Αναπαράσταση δεδομένων ταχύτητας προφίλ
|
||||||
|
Comment[en_GB]=Visualisation of Performance Profiling Data
|
||||||
|
Comment[es]=Visualización de datos de análisis de rendimiento
|
||||||
|
Comment[et]=Jõudluse profileerimise andmete visualiseerimise vahend
|
||||||
|
Comment[eu]=Errendimendu profil datuen bistaratzea
|
||||||
|
Comment[fa]=تجسم کارایی گزارش دادهها
|
||||||
|
Comment[fi]=Visualisointi tehokkuusprofiloinnin tiedoista
|
||||||
|
Comment[fr]=Visualise les données de profilage de performances
|
||||||
|
Comment[ga]=Amharcléiriú ar Shonraí Próifílithe Feidhmíochta
|
||||||
|
Comment[gl]=Visualización dos datos da análise de rendemento
|
||||||
|
Comment[hu]=Teljesítményprofil-adatok megjelenítése
|
||||||
|
Comment[is]=Sjónræn framsetning gagna úr afkastakönnun
|
||||||
|
Comment[it]=Visualizzazione dei dati di profilo delle prestazioni
|
||||||
|
Comment[ja]=パフォーマンスプロファイルデータを視覚化
|
||||||
|
Comment[kk]=Деректерді профильдеудің визуализациясы
|
||||||
|
Comment[km]=ការមើលឃើញការអនុវត្តរបស់ទិន្នន័យទម្រង់
|
||||||
|
Comment[ko]=성능 프로파일링 데이터 시각화
|
||||||
|
Comment[lt]=Veikimo profiliavimo duomenų vizualizacija
|
||||||
|
Comment[lv]=Veiktspējas profilēšanas datu vizualizācija
|
||||||
|
Comment[mr]=कार्यक्षमता प्रोफाइलिंग डेटाचे व्हिज्युअलायझेशन
|
||||||
|
Comment[nb]=Vis informasjon om ytelse.
|
||||||
|
Comment[nds]=Visualiseren vun Programmleisten-Looptietdaten
|
||||||
|
Comment[ne]=सम्पादन प्रोफाइलिङ डाटाको दृष्टिकरण
|
||||||
|
Comment[nl]=Visualisatie van Performance Profiling Data
|
||||||
|
Comment[nn]=Vis informasjon om yting
|
||||||
|
Comment[pl]=Wizualizacja danych profilowania wydajności
|
||||||
|
Comment[pt]=Visualização dos Dados de Análise de Performance
|
||||||
|
Comment[pt_BR]=Visualização dos dados de análise de desempenho
|
||||||
|
Comment[ru]=Утилита для визуального профилирования приложений
|
||||||
|
Comment[sk]=Vizualizácia dát o výkone
|
||||||
|
Comment[sl]=Vizualizacija podatkov profilnih zmogljivosti
|
||||||
|
Comment[sr]=Визуелизација профилисања перформанси
|
||||||
|
Comment[sr@ijekavian]=Визуелизација профилисања перформанси
|
||||||
|
Comment[sr@ijekavianlatin]=Vizuelizacija profilisanja performansi
|
||||||
|
Comment[sr@latin]=Vizuelizacija profilisanja performansi
|
||||||
|
Comment[sv]=Åskådliggörande av profileringsdata för prestanda
|
||||||
|
Comment[ta]= விவர தகவலை செயல்பாட்டு காட்சியாளிப்பு
|
||||||
|
Comment[tg]=Утилита барои гузориши профили визуалӣ
|
||||||
|
Comment[tr]=Performans Profilleme Verisinin Görünür Hali
|
||||||
|
Comment[ug]=سانلىق-مەلۇماتلارنىڭ سەپلىنىشىنى سۈرەتلەشتۈرۈش
|
||||||
|
Comment[uk]=Візуалізація даних профілювання швидкодії
|
||||||
|
Comment[x-test]=xxVisualization of Performance Profiling Dataxx
|
||||||
|
Comment[zh_CN]=性能测试数据的可视化表现
|
||||||
|
Comment[zh_TW]=效能分析資料視覺化
|
||||||
|
X-DBUS-StartupType=Multi
|
||||||
|
X-DBUS-ServiceName=net.sf.kcachegrind
|
||||||
|
Categories=Qt;KDE;Development;
|
57
kcachegrind/kcachegrind/kcachegrindui.rc
Normal file
57
kcachegrind/kcachegrind/kcachegrindui.rc
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<!DOCTYPE kpartgui>
|
||||||
|
<kpartgui name="kcachegrind" version="4">
|
||||||
|
<MenuBar>
|
||||||
|
<Menu name="file"><text>&File</text>
|
||||||
|
<Action name="file_add" append="open_merge"/>
|
||||||
|
<Action name="reload" append="revert_merge"/>
|
||||||
|
<Action name="dump" append="revert_merge"/>
|
||||||
|
<Action name="export"/>
|
||||||
|
</Menu>
|
||||||
|
<Menu name="view"><text>&View</text>
|
||||||
|
<Action name="view_cost_type"/>
|
||||||
|
<Action name="view_cost_type2"/>
|
||||||
|
<Action name="view_group_type"/>
|
||||||
|
<Separator/>
|
||||||
|
<Menu name="layouts"><text>&Layout</text>
|
||||||
|
<Action name="layout_next"/>
|
||||||
|
<Action name="layout_previous"/>
|
||||||
|
<Action name="layout_duplicate"/>
|
||||||
|
<Action name="layout_remove"/>
|
||||||
|
<Separator/>
|
||||||
|
<Action name="layout_restore"/>
|
||||||
|
<Action name="layout_save"/>
|
||||||
|
</Menu>
|
||||||
|
<Action name="view_split"/>
|
||||||
|
<Action name="view_split_dir"/>
|
||||||
|
<Separator/>
|
||||||
|
<Action name="view_percentage"/>
|
||||||
|
<Action name="view_expanded"/>
|
||||||
|
<Action name="view_cycles"/>
|
||||||
|
<Action name="hide_templates"/>
|
||||||
|
</Menu>
|
||||||
|
<Menu name="settings">
|
||||||
|
<Menu append="show_toolbar_merge"><text>Sidebars</text>
|
||||||
|
<Action name="settings_show_dumpdock"/>
|
||||||
|
<Action name="settings_show_partdock"/>
|
||||||
|
<Action name="settings_show_stackdock"/>
|
||||||
|
<Action name="settings_show_profiledock"/>
|
||||||
|
</Menu>
|
||||||
|
</Menu>
|
||||||
|
</MenuBar>
|
||||||
|
|
||||||
|
<ToolBar name="mainToolBar" noMerge="1"><text>Main Toolbar</text>
|
||||||
|
<Action name="file_open"/>
|
||||||
|
<Separator/>
|
||||||
|
<Action name="go_back"/>
|
||||||
|
<Action name="go_forward"/>
|
||||||
|
<Action name="go_up"/>
|
||||||
|
<Separator/>
|
||||||
|
<Action name="view_percentage"/>
|
||||||
|
<Action name="view_cycles"/>
|
||||||
|
<Action name="view_expanded"/>
|
||||||
|
<Action name="hide_templates"/>
|
||||||
|
</ToolBar>
|
||||||
|
<ToolBar name="stateToolBar" noMerge="1"><text>State Toolbar</text>
|
||||||
|
<Action name="view_cost_type"/>
|
||||||
|
</ToolBar>
|
||||||
|
</kpartgui>
|
148
kcachegrind/kcachegrind/kdeconfig.cpp
Normal file
148
kcachegrind/kcachegrind/kdeconfig.cpp
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002, 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configuration for KCachegrind
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kdeconfig.h"
|
||||||
|
|
||||||
|
#include <kconfig.h>
|
||||||
|
#include <klocale.h>
|
||||||
|
#include <kdebug.h>
|
||||||
|
#include <kconfiggroup.h>
|
||||||
|
|
||||||
|
#include "tracedata.h"
|
||||||
|
#include "traceitemview.h"
|
||||||
|
#include "ui_configdlgbase.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// KDEConfigGroup
|
||||||
|
//
|
||||||
|
|
||||||
|
KDEConfigGroup::KDEConfigGroup(KConfigGroup* group, bool readOnly)
|
||||||
|
{
|
||||||
|
_kgroup = group;
|
||||||
|
_readOnly = readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
KDEConfigGroup::~KDEConfigGroup()
|
||||||
|
{
|
||||||
|
delete _kgroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KDEConfigGroup::setValue(const QString& key, const QVariant& value,
|
||||||
|
const QVariant& defaultValue)
|
||||||
|
{
|
||||||
|
if ((_kgroup == 0) || _readOnly) return;
|
||||||
|
|
||||||
|
if (value == defaultValue) {
|
||||||
|
_kgroup->deleteEntry(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(value.type()) {
|
||||||
|
case QVariant::Bool:
|
||||||
|
_kgroup->writeEntry(key, value.toBool());
|
||||||
|
break;
|
||||||
|
case QVariant::Int:
|
||||||
|
_kgroup->writeEntry(key, value.toInt());
|
||||||
|
break;
|
||||||
|
case QVariant::Double:
|
||||||
|
_kgroup->writeEntry(key, value.toDouble());
|
||||||
|
break;
|
||||||
|
case QVariant::String:
|
||||||
|
_kgroup->writeEntry(key, value.toString());
|
||||||
|
break;
|
||||||
|
case QVariant::StringList:
|
||||||
|
_kgroup->writeEntry(key, value.toStringList());
|
||||||
|
break;
|
||||||
|
case QVariant::Color:
|
||||||
|
_kgroup->writeEntry(key, value.value<QColor>());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qFatal("KDEConfigGroup::setValue - QVariant type %s not supported",
|
||||||
|
value.typeName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant KDEConfigGroup::value(const QString& key,
|
||||||
|
const QVariant& defaultValue) const
|
||||||
|
{
|
||||||
|
if (_kgroup == 0) return defaultValue;
|
||||||
|
|
||||||
|
switch(defaultValue.type()) {
|
||||||
|
case QVariant::Bool:
|
||||||
|
return QVariant(_kgroup->readEntry(key,
|
||||||
|
defaultValue.toBool()));
|
||||||
|
case QVariant::Int:
|
||||||
|
return QVariant(_kgroup->readEntry(key,
|
||||||
|
defaultValue.toInt()));
|
||||||
|
case QVariant::Double:
|
||||||
|
return QVariant(_kgroup->readEntry(key,
|
||||||
|
defaultValue.toDouble()));
|
||||||
|
case QVariant::String:
|
||||||
|
return QVariant(_kgroup->readEntry(key,
|
||||||
|
defaultValue.toString()));
|
||||||
|
case QVariant::StringList:
|
||||||
|
return QVariant(_kgroup->readEntry(key,
|
||||||
|
defaultValue.toStringList()));
|
||||||
|
case QVariant::Color:
|
||||||
|
return QVariant(_kgroup->readEntry(key,
|
||||||
|
defaultValue.value<QColor>()));
|
||||||
|
default:
|
||||||
|
qFatal("KDEConfigGroup::value - QVariant type %s not supported",
|
||||||
|
defaultValue.typeName());
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// KDEConfigStorage
|
||||||
|
//
|
||||||
|
|
||||||
|
KDEConfigStorage::KDEConfigStorage(KConfig* kconfig)
|
||||||
|
{
|
||||||
|
_kconfig = kconfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigGroup* KDEConfigStorage::getGroup(const QString& group,
|
||||||
|
const QString& optSuffix)
|
||||||
|
{
|
||||||
|
KConfigGroup* g;
|
||||||
|
bool readOnly;
|
||||||
|
|
||||||
|
if (!optSuffix.isEmpty()) {
|
||||||
|
readOnly = true;
|
||||||
|
QStringList gList = _kconfig->groupList();
|
||||||
|
if (gList.contains(group+optSuffix))
|
||||||
|
g = new KConfigGroup(_kconfig, group+optSuffix);
|
||||||
|
else if (gList.contains(group))
|
||||||
|
g = new KConfigGroup(_kconfig, group);
|
||||||
|
else
|
||||||
|
g = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
readOnly = false;
|
||||||
|
g = new KConfigGroup(_kconfig, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new KDEConfigGroup(g, readOnly);
|
||||||
|
}
|
63
kcachegrind/kcachegrind/kdeconfig.h
Normal file
63
kcachegrind/kcachegrind/kdeconfig.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002, 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configuration for KCachegrind
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KDECONFIG_H
|
||||||
|
#define KDECONFIG_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
class KConfig;
|
||||||
|
class KConfigGroup;
|
||||||
|
class KDEConfigStorage;
|
||||||
|
|
||||||
|
class KDEConfigGroup: public ConfigGroup
|
||||||
|
{
|
||||||
|
friend class KDEConfigStorage;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~KDEConfigGroup();
|
||||||
|
|
||||||
|
void setValue(const QString& key, const QVariant& value,
|
||||||
|
const QVariant& defaultValue = QVariant());
|
||||||
|
QVariant value(const QString& key, const QVariant& defaultValue) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
KDEConfigGroup(KConfigGroup*, bool);
|
||||||
|
|
||||||
|
KConfigGroup* _kgroup;
|
||||||
|
bool _readOnly;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class KDEConfigStorage : public ConfigStorage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KDEConfigStorage(KConfig*);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConfigGroup* getGroup(const QString& group,
|
||||||
|
const QString& optSuffix);
|
||||||
|
|
||||||
|
KConfig* _kconfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
99
kcachegrind/kcachegrind/main.cpp
Normal file
99
kcachegrind/kcachegrind/main.cpp
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KCachegrind startup
|
||||||
|
*/
|
||||||
|
|
||||||
|
// for KCACHEGRIND_VERSION
|
||||||
|
#include "../version.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <kapplication.h>
|
||||||
|
#include <kcmdlineargs.h>
|
||||||
|
#include <kaboutdata.h>
|
||||||
|
#include <klocale.h>
|
||||||
|
|
||||||
|
#include "kdeconfig.h"
|
||||||
|
#include "toplevel.h"
|
||||||
|
#include "tracedata.h"
|
||||||
|
#include "loader.h"
|
||||||
|
|
||||||
|
int main( int argc, char ** argv )
|
||||||
|
{
|
||||||
|
KAboutData aboutData("kcachegrind", 0,
|
||||||
|
ki18n("KCachegrind"),
|
||||||
|
KCACHEGRIND_VERSION,
|
||||||
|
ki18n("KDE Frontend for Callgrind/Cachegrind"),
|
||||||
|
KAboutData::License_GPL,
|
||||||
|
ki18n("(C) 2002 - 2011"), KLocalizedString(),
|
||||||
|
"http://kcachegrind.sf.net");
|
||||||
|
aboutData.addAuthor(ki18n("Josef Weidendorfer"),
|
||||||
|
ki18n("Author/Maintainer"),
|
||||||
|
"Josef.Weidendorfer@gmx.de");
|
||||||
|
|
||||||
|
KCmdLineArgs::init(argc, argv, &aboutData);
|
||||||
|
|
||||||
|
KCmdLineOptions options;
|
||||||
|
//options.add("r <exec>", ki18n("Run <exec> under cachegrind"));
|
||||||
|
options.add("+[trace]", ki18n("Show information of this trace"));
|
||||||
|
KCmdLineArgs::addCmdLineOptions( options );
|
||||||
|
|
||||||
|
KApplication a;
|
||||||
|
KGlobal::locale()->insertCatalog("kcachegrind_qt");
|
||||||
|
TopLevel* t;
|
||||||
|
Loader::initLoaders();
|
||||||
|
|
||||||
|
KConfig* kc = KGlobal::config().data();
|
||||||
|
ConfigStorage::setStorage(new KDEConfigStorage(kc));
|
||||||
|
|
||||||
|
if (a.isSessionRestored()){
|
||||||
|
int n = 1;
|
||||||
|
while (KMainWindow::canBeRestored(n)){
|
||||||
|
(new TopLevel())->restore(n);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
|
||||||
|
int nbArgs = args->count();
|
||||||
|
if (nbArgs>0) {
|
||||||
|
t = new TopLevel();
|
||||||
|
t->show();
|
||||||
|
for(int i = 0; i < nbArgs; i++) {
|
||||||
|
t->loadDelayed(args->arg(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// load trace in current dir
|
||||||
|
t = new TopLevel();
|
||||||
|
t->show();
|
||||||
|
t->loadDelayed(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) );
|
||||||
|
int res = a.exec();
|
||||||
|
|
||||||
|
// to make leak checking in valgrind happy...
|
||||||
|
Loader::deleteLoaders();
|
||||||
|
ProfileContext::cleanup();
|
||||||
|
ConfigStorage::cleanup();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
141
kcachegrind/kcachegrind/tips
Normal file
141
kcachegrind/kcachegrind/tips
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
<tip category="KCachegrind|Help">
|
||||||
|
<html>
|
||||||
|
<p>...that the <em>What's this...</em> help for every GUI widget
|
||||||
|
in KCachegrind contains detailed usage information for this widget?
|
||||||
|
It is highly recommend to read at least these help texts on first
|
||||||
|
use. Request <em>What's this...</em> help by pressing
|
||||||
|
Shift-F1 and clicking on the widget.</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Explanation">
|
||||||
|
<html>
|
||||||
|
<p>...that you can get profile information at instruction level
|
||||||
|
with Calltree when you provide the option <em>--dump-instr=yes</em>?
|
||||||
|
Use the Assembler View for the instruction annotations.
|
||||||
|
</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Keyboard">
|
||||||
|
<html>
|
||||||
|
<p>...that you can use Alt-Left/Right keys of your keyboard to go
|
||||||
|
back/forward in the active object history ?</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Keyboard">
|
||||||
|
<html>
|
||||||
|
<p>...that you can navigate in the Callee/Caller Map View using
|
||||||
|
arrow keys? Use Left/Right to change to siblings of the current
|
||||||
|
item; use Up/Down to go one nesting level up/down. To select
|
||||||
|
the current item, press Space, and to activate it, press Return.
|
||||||
|
</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Keyboard">
|
||||||
|
<html>
|
||||||
|
<p>...that you can navigate in the Call Graph View using
|
||||||
|
arrow keys? Use Up/Down to go one calling level up/down, alternating
|
||||||
|
between calls and functions. Use Left/Right to change to siblings of a current
|
||||||
|
selected call. To activate the current item, press Return.
|
||||||
|
</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Filters">
|
||||||
|
<html>
|
||||||
|
<p>...that you can rapidly locate a function by entering part of its
|
||||||
|
name (case-insensitive) into the edit line of the toolbar
|
||||||
|
and hit return?</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Appearance">
|
||||||
|
<html>
|
||||||
|
<p>...that you can assign custom colors to
|
||||||
|
ELF objects/C++ Classes/Source Files for graph coloring
|
||||||
|
in <em>Settings->Configure KCachegrind...</em>?</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Configuration">
|
||||||
|
<html>
|
||||||
|
<p>...that you can see if debug info is available for a selected
|
||||||
|
function by looking at the location label in the Info tab or
|
||||||
|
the source listing header in the source tab?</p>
|
||||||
|
<p>There must be the name of the source file (with extension).
|
||||||
|
If KCachegrind still does not show the source, make sure that you
|
||||||
|
have added the directory of the source file to the
|
||||||
|
<em>Source Directories</em> list in the configuration.
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Appearance">
|
||||||
|
<html>
|
||||||
|
<p>...that you can configure whether KCachgrind should
|
||||||
|
show absolute event counts or relative ones (percentage display)?</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Appearance">
|
||||||
|
<html>
|
||||||
|
<p>...that you can configure the maximum number of items
|
||||||
|
for all function lists in KCachegrind? Limiting the number
|
||||||
|
of items is done to get a fast reacting GUI. The last item in
|
||||||
|
the list will show you the number of skipped functions, together
|
||||||
|
with a cost condition for these skipped functions.</p>
|
||||||
|
<p>To activate a function with small costs, search for it and select
|
||||||
|
it in the flat profile. Selecting functions with small cost will
|
||||||
|
temporarily add them to the flat profile list.</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Explanation">
|
||||||
|
<html>
|
||||||
|
<p>...that the Coverage tab - in contrast to the Call Lists tab -
|
||||||
|
shows <em>all</em> functions that are calling the selected function
|
||||||
|
(upper part) / are called by the selected function (bottom part),
|
||||||
|
no matter how many function are between them on the stack?</p>
|
||||||
|
<p>Examples:</p>
|
||||||
|
<p>An entry in the upper list for function foo1() with a value of 50%
|
||||||
|
with function bar() selected means that 50% of all the cost of function
|
||||||
|
bar() happened while called from function foo1().</p>
|
||||||
|
<p>An entry in the bottom list for function foo2() with a value of 50%
|
||||||
|
with function bar() selected means that 50% of all the cost of function
|
||||||
|
bar() happened while calling foo2() from bar().</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Explanation">
|
||||||
|
<html>
|
||||||
|
<p>...that waiting for the tool tip inside of a tree map
|
||||||
|
shows the list of names of the nested rectangles the mouse
|
||||||
|
pointer is over?</p>
|
||||||
|
<p>Items from this list can be selected by pressing the right
|
||||||
|
mouse button.</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Explanation">
|
||||||
|
<html>
|
||||||
|
<p>...that you can constrain the cost counts shown to only a
|
||||||
|
few parts of the whole trace by selecting these parts in the
|
||||||
|
"Trace Selection" Dockable?</p>
|
||||||
|
<p>To generate multiple parts in a profiling run with
|
||||||
|
cachegrind, use e.g. option --cachedumps=xxx for parts
|
||||||
|
of a length of xxx basic blocks (A basic block is a run
|
||||||
|
of not-branching assembler statements inside of your program
|
||||||
|
code).</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
||||||
|
<tip category="KCachegrind|Explanation">
|
||||||
|
<p>...that by splitting the view to show information of
|
||||||
|
two functions simultaniously, selecting a function in
|
||||||
|
one panel shows the information for that function
|
||||||
|
in the other panel?</p>
|
||||||
|
</html>
|
||||||
|
</tip>
|
||||||
|
|
2381
kcachegrind/kcachegrind/toplevel.cpp
Normal file
2381
kcachegrind/kcachegrind/toplevel.cpp
Normal file
File diff suppressed because it is too large
Load diff
296
kcachegrind/kcachegrind/toplevel.h
Normal file
296
kcachegrind/kcachegrind/toplevel.h
Normal file
|
@ -0,0 +1,296 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002, 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KCachegrind top level window
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TOPLEVEL_H
|
||||||
|
#define TOPLEVEL_H
|
||||||
|
|
||||||
|
#include <qdatetime.h>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
#include <kxmlguiwindow.h>
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
#include "traceitemview.h"
|
||||||
|
#include "tracedata.h"
|
||||||
|
#include "toplevelbase.h"
|
||||||
|
|
||||||
|
class MultiView;
|
||||||
|
class QLineEdit;
|
||||||
|
class QDockWidget;
|
||||||
|
class QLabel;
|
||||||
|
class QProgressBar;
|
||||||
|
class QMenu;
|
||||||
|
|
||||||
|
class KUrl;
|
||||||
|
class KSelectAction;
|
||||||
|
class KToggleAction;
|
||||||
|
class KToolBarPopupAction;
|
||||||
|
|
||||||
|
class TraceData;
|
||||||
|
class KRecentFilesAction;
|
||||||
|
class MainWidget;
|
||||||
|
class PartSelection;
|
||||||
|
class FunctionSelection;
|
||||||
|
class DumpSelection;
|
||||||
|
class StackSelection;
|
||||||
|
class TraceFunction;
|
||||||
|
|
||||||
|
class TopLevel : public KXmlGuiWindow, public Logger, public TopLevelBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_CLASSINFO("D-Bus Interface", "org.kde.kcachegrind")
|
||||||
|
public:
|
||||||
|
TopLevel();
|
||||||
|
~TopLevel();
|
||||||
|
|
||||||
|
TraceData* data() { return _data; }
|
||||||
|
void setData(TraceData*);
|
||||||
|
|
||||||
|
virtual void saveProperties(KConfigGroup &);
|
||||||
|
virtual void readProperties(const KConfigGroup &);
|
||||||
|
|
||||||
|
void createActions();
|
||||||
|
void createDocks();
|
||||||
|
|
||||||
|
ProfileContext::Type groupType() { return _groupType; }
|
||||||
|
EventType* eventType() { return _eventType; }
|
||||||
|
EventType* eventType2() { return _eventType2; }
|
||||||
|
TracePartList activeParts() { return _activeParts; }
|
||||||
|
TracePartList hiddenParts() { return _hiddenParts; }
|
||||||
|
|
||||||
|
// current config
|
||||||
|
bool showPercentage() const { return _showPercentage; }
|
||||||
|
bool showExpanded() const { return _showExpanded; }
|
||||||
|
bool showCycles() const { return _showCycles; }
|
||||||
|
|
||||||
|
/* convenience functions for often used context menu items */
|
||||||
|
void addEventTypeMenu(QMenu*,bool);
|
||||||
|
void addGoMenu(QMenu*);
|
||||||
|
|
||||||
|
// Logger overwrites: notifications for file loading
|
||||||
|
virtual void loadStart(const QString& filename);
|
||||||
|
virtual void loadProgress(int progress); // 0 - 100
|
||||||
|
virtual void loadWarning(int line, const QString& msg);
|
||||||
|
virtual void loadError(int line, const QString& msg);
|
||||||
|
virtual void loadFinished(const QString& msg); // msg could be error
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void load();
|
||||||
|
void load(const KUrl&);
|
||||||
|
void load(QString);
|
||||||
|
void add();
|
||||||
|
void add(const KUrl&);
|
||||||
|
void add(QString);
|
||||||
|
|
||||||
|
// for quickly showing the main window...
|
||||||
|
void loadDelayed(QString);
|
||||||
|
void loadDelayed(QStringList);
|
||||||
|
|
||||||
|
void reload();
|
||||||
|
void exportGraph();
|
||||||
|
void newWindow();
|
||||||
|
void configure();
|
||||||
|
void querySlot();
|
||||||
|
void dummySlot();
|
||||||
|
|
||||||
|
// layouts
|
||||||
|
void layoutDuplicate();
|
||||||
|
void layoutRemove();
|
||||||
|
void layoutNext();
|
||||||
|
void layoutPrevious();
|
||||||
|
void layoutSave();
|
||||||
|
void layoutRestore();
|
||||||
|
void updateLayoutActions();
|
||||||
|
|
||||||
|
void updateStatusBar();
|
||||||
|
void eventTypeSelected(const QString&);
|
||||||
|
void eventType2Selected(const QString&);
|
||||||
|
void groupTypeSelected(int);
|
||||||
|
void splitSlot();
|
||||||
|
void splitDirSlot();
|
||||||
|
void configureToolbars();
|
||||||
|
void configureKeys();
|
||||||
|
bool queryClose();
|
||||||
|
void togglePartDock();
|
||||||
|
void toggleStackDock();
|
||||||
|
void toggleFunctionDock();
|
||||||
|
void toggleDumpDock();
|
||||||
|
void toggleStatusBar();
|
||||||
|
void partVisibilityChanged(bool);
|
||||||
|
void dumpVisibilityChanged(bool);
|
||||||
|
void stackVisibilityChanged(bool);
|
||||||
|
void functionVisibilityChanged(bool);
|
||||||
|
void togglePercentage();
|
||||||
|
void setPercentage(bool);
|
||||||
|
void setAbsoluteCost();
|
||||||
|
void setRelativeCost();
|
||||||
|
void toggleExpanded();
|
||||||
|
void toggleCycles();
|
||||||
|
void toggleHideTemplates();
|
||||||
|
void forceTrace();
|
||||||
|
void forwardAboutToShow();
|
||||||
|
void forwardTriggered(QAction*);
|
||||||
|
void backAboutToShow();
|
||||||
|
void backTriggered(QAction*);
|
||||||
|
void upAboutToShow();
|
||||||
|
void upTriggered(QAction*);
|
||||||
|
|
||||||
|
bool setEventType(EventType*);
|
||||||
|
bool setEventType2(EventType*);
|
||||||
|
bool setEventType(QString);
|
||||||
|
bool setEventType2(QString);
|
||||||
|
bool setEventType(QAction*);
|
||||||
|
bool setEventType2(QAction*);
|
||||||
|
bool setGroupType(ProfileContext::Type);
|
||||||
|
bool setGroupType(QString);
|
||||||
|
bool setGroup(TraceCostItem*);
|
||||||
|
bool setGroup(QString);
|
||||||
|
bool setFunction(TraceFunction*);
|
||||||
|
bool setFunction(QString);
|
||||||
|
void activePartsChangedSlot(const TracePartList& list);
|
||||||
|
void partsHideSelectedSlot();
|
||||||
|
void partsUnhideAllSlot();
|
||||||
|
|
||||||
|
/* These go back to mainloop first by using a timer.
|
||||||
|
* So they can be called from event handlers that
|
||||||
|
* are not allowed to delete list entries.
|
||||||
|
*/
|
||||||
|
void setEventTypeDelayed(EventType*);
|
||||||
|
void setEventType2Delayed(EventType*);
|
||||||
|
void setGroupTypeDelayed(ProfileContext::Type);
|
||||||
|
void setGroupDelayed(TraceCostItem*);
|
||||||
|
void setTraceItemDelayed(CostItem*);
|
||||||
|
void partsHideSelectedSlotDelayed();
|
||||||
|
void partsUnhideAllSlotDelayed();
|
||||||
|
void goBack();
|
||||||
|
void goForward();
|
||||||
|
void goUp();
|
||||||
|
void setDirectionDelayed(TraceItemView::Direction);
|
||||||
|
|
||||||
|
/* SingleShot Slots (without parameters) for the delayed versions */
|
||||||
|
void setEventTypeDelayed();
|
||||||
|
void setEventType2Delayed();
|
||||||
|
void setGroupTypeDelayed();
|
||||||
|
void setGroupDelayed();
|
||||||
|
void setTraceItemDelayed();
|
||||||
|
void loadTraceDelayed();
|
||||||
|
void setDirectionDelayed();
|
||||||
|
|
||||||
|
// configuration has changed
|
||||||
|
void configChanged();
|
||||||
|
|
||||||
|
//void refresh();
|
||||||
|
void slotShowTipOnStart();
|
||||||
|
void slotShowTip();
|
||||||
|
|
||||||
|
// progress in status bar, empty message disables progress display
|
||||||
|
void showStatus(const QString& msg, int progress);
|
||||||
|
void showMessage(const QString&, int msec);
|
||||||
|
|
||||||
|
// for running callgrind_control in the background
|
||||||
|
void ccReadOutput();
|
||||||
|
void ccError(QProcess::ProcessError);
|
||||||
|
void ccExit(int,QProcess::ExitStatus);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void resetState();
|
||||||
|
void createLayoutActions();
|
||||||
|
void createMiscActions();
|
||||||
|
void setupMainWidget(MainWidget*);
|
||||||
|
void setupPartSelection(PartSelection*);
|
||||||
|
void restoreCurrentState(const QString& postfix);
|
||||||
|
void saveCurrentState(const QString& postfix);
|
||||||
|
void saveTraceSettings();
|
||||||
|
QString traceKey();
|
||||||
|
void restoreTraceTypes();
|
||||||
|
void restoreTraceSettings();
|
||||||
|
void updateViewsOnChange(int);
|
||||||
|
/// open @p file, might be compressed
|
||||||
|
/// @return true when the file could be opened, false otherwise.
|
||||||
|
bool openDataFile(const QString& file);
|
||||||
|
|
||||||
|
KStatusBar* _statusbar;
|
||||||
|
QLabel* _statusLabel;
|
||||||
|
KRecentFilesAction* _openRecent;
|
||||||
|
bool _twoMainWidgets;
|
||||||
|
Qt::Orientation _spOrientation;
|
||||||
|
|
||||||
|
MultiView* _multiView;
|
||||||
|
FunctionSelection* _functionSelection;
|
||||||
|
DumpSelection* _dumpSelection;
|
||||||
|
PartSelection* _partSelection;
|
||||||
|
StackSelection* _stackSelection;
|
||||||
|
QLineEdit* queryLineEdit;
|
||||||
|
|
||||||
|
QDockWidget *_partDock, *_stackDock, *_functionDock, *_dumpDock;
|
||||||
|
bool _forcePartDock;
|
||||||
|
|
||||||
|
KSelectAction *_saCost, *_saCost2, *saGroup;
|
||||||
|
KToggleAction *_partDockShown, *_stackDockShown;
|
||||||
|
KToggleAction *_functionDockShown, *_dumpDockShown;
|
||||||
|
KToggleAction *_taPercentage, *_taExpanded, *_taCycles, *_taHideTemplates;
|
||||||
|
KToggleAction *_taDump, *_taSplit, *_taSplitDir;
|
||||||
|
KToolBarPopupAction *_paForward, *_paBack, *_paUp;
|
||||||
|
|
||||||
|
TraceFunction* _function;
|
||||||
|
const QObject* _lastSender;
|
||||||
|
|
||||||
|
// trace data shown in this window
|
||||||
|
TraceData* _data;
|
||||||
|
// subcost types used for visualization
|
||||||
|
EventType* _eventType;
|
||||||
|
EventType* _eventType2;
|
||||||
|
// grouping of function list
|
||||||
|
ProfileContext::Type _groupType;
|
||||||
|
// selected group
|
||||||
|
TraceCostItem* _group;
|
||||||
|
// selected parts
|
||||||
|
TracePartList _activeParts;
|
||||||
|
// hidden parts
|
||||||
|
TracePartList _hiddenParts;
|
||||||
|
// layouts
|
||||||
|
int _layoutCurrent, _layoutCount;
|
||||||
|
|
||||||
|
// for delayed slots
|
||||||
|
EventType* _eventTypeDelayed;
|
||||||
|
EventType* _eventType2Delayed;
|
||||||
|
ProfileContext::Type _groupTypeDelayed;
|
||||||
|
TraceCostItem* _groupDelayed;
|
||||||
|
CostItem* _traceItemDelayed;
|
||||||
|
QStringList _loadFilesDelayed;
|
||||||
|
TraceItemView::Direction _directionDelayed;
|
||||||
|
|
||||||
|
// for status progress display
|
||||||
|
QString _progressMsg;
|
||||||
|
QTime _progressStart;
|
||||||
|
QProgressBar* _progressBar;
|
||||||
|
|
||||||
|
// toplevel configuration options
|
||||||
|
bool _showPercentage, _showExpanded, _showCycles, _hideTemplates;
|
||||||
|
|
||||||
|
// for running callgrind_control in the background
|
||||||
|
QProcess* _ccProcess;
|
||||||
|
QString _ccOutput;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
19
kcachegrind/libcore/BUGS
Normal file
19
kcachegrind/libcore/BUGS
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Visualization state problem (16.12.08)
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
A view on profile data should use its own visualization state, such
|
||||||
|
as currently selected/activated item, attributes such as
|
||||||
|
absolute/percentage and so on.
|
||||||
|
A view can be console output, a toplevel window (we want to support
|
||||||
|
multiple for the same data!), or even subviews inside of a view
|
||||||
|
(different cost item activations (REALLY NEEDED)).
|
||||||
|
|
||||||
|
Currently, this visualization state is stored either
|
||||||
|
* in the single Configuration object
|
||||||
|
* attribute of toplevel windows (eg. options from tool bar)
|
||||||
|
|
||||||
|
Problems:
|
||||||
|
* Which configuration options persistent between runs of the app?
|
||||||
|
|
||||||
|
Todo:
|
||||||
|
* Use separate VisualizationState objects
|
20
kcachegrind/libcore/CMakeLists.txt
Normal file
20
kcachegrind/libcore/CMakeLists.txt
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
set(core_SRCS
|
||||||
|
context.cpp
|
||||||
|
costitem.cpp
|
||||||
|
eventtype.cpp
|
||||||
|
subcost.cpp
|
||||||
|
addr.cpp
|
||||||
|
tracedata.cpp
|
||||||
|
loader.cpp
|
||||||
|
cachegrindloader.cpp
|
||||||
|
fixcost.cpp
|
||||||
|
pool.cpp
|
||||||
|
coverage.cpp
|
||||||
|
stackbrowser.cpp
|
||||||
|
utils.cpp
|
||||||
|
logger.cpp
|
||||||
|
config.cpp
|
||||||
|
globalconfig.cpp )
|
||||||
|
|
||||||
|
qt4_automoc(${core_SRCS})
|
||||||
|
add_library(core STATIC ${core_SRCS})
|
92
kcachegrind/libcore/addr.cpp
Normal file
92
kcachegrind/libcore/addr.cpp
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002 - 2009 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "addr.h"
|
||||||
|
|
||||||
|
//---------------------------------------------------
|
||||||
|
// Addr
|
||||||
|
|
||||||
|
bool Addr::set(FixString& s)
|
||||||
|
{
|
||||||
|
return s.stripUInt64(_v);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Addr::set(const char *s)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
_v = 0;
|
||||||
|
|
||||||
|
while((n<16) && *s) {
|
||||||
|
if ((*s>='0') && (*s<='9'))
|
||||||
|
_v = 16*_v + (*s-'0');
|
||||||
|
else if ((*s>='a') && (*s<='f'))
|
||||||
|
_v = 16*_v + 10 + (*s-'a');
|
||||||
|
else if ((*s>='A') && (*s<='F'))
|
||||||
|
_v = 16*_v + 10 + (*s-'A');
|
||||||
|
else break;
|
||||||
|
s++;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString Addr::toString() const
|
||||||
|
{
|
||||||
|
if (_v == 0) return QString("0");
|
||||||
|
|
||||||
|
uint64 n = _v;
|
||||||
|
QString hex;
|
||||||
|
hex.reserve(16);
|
||||||
|
|
||||||
|
while(n>0) {
|
||||||
|
int d = (n & 15);
|
||||||
|
hex = QChar((d<10) ? ('0'+d) : ('A'-10+d)) + hex;
|
||||||
|
n /= 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Addr::pretty() const
|
||||||
|
{
|
||||||
|
if (_v == 0) return QString("0");
|
||||||
|
|
||||||
|
uint64 n = _v;
|
||||||
|
int p = 0;
|
||||||
|
QString hex;
|
||||||
|
hex.reserve(20);
|
||||||
|
|
||||||
|
while(n>0) {
|
||||||
|
int d = (n & 15);
|
||||||
|
if ((p>0) && ((p%4)==0)) hex = ' ' + hex;
|
||||||
|
hex = QChar((d<10) ? ('0'+d) : ('A'-10+d)) + hex;
|
||||||
|
n /= 16;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Addr::isInRange(Addr a, int distance)
|
||||||
|
{
|
||||||
|
uint64 diff = (a._v > _v) ? (a._v - _v) : (_v - a._v);
|
||||||
|
uint64 dist = (distance<0) ? distance : -distance;
|
||||||
|
return (diff < dist);
|
||||||
|
}
|
62
kcachegrind/libcore/addr.h
Normal file
62
kcachegrind/libcore/addr.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002 - 2009 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ADDR_H
|
||||||
|
#define ADDR_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Addresses are 64bit values like costs to be able
|
||||||
|
* to always load profile data produced on 64bit
|
||||||
|
* architectures.
|
||||||
|
*/
|
||||||
|
class Addr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Addr() { _v=0; }
|
||||||
|
Addr(uint64 v) { _v = v; }
|
||||||
|
|
||||||
|
// Interpretes char data at s as hex (without "0x" prefix)
|
||||||
|
// and return number of interpreted chars.
|
||||||
|
int set(const char *s);
|
||||||
|
bool set(FixString& s);
|
||||||
|
QString toString() const;
|
||||||
|
// similar to toString(), but adds a space every 4 digits
|
||||||
|
QString pretty() const;
|
||||||
|
|
||||||
|
// returns true if this address is in [a-distance;a+distance]
|
||||||
|
bool isInRange(Addr a, int distance);
|
||||||
|
|
||||||
|
bool operator==(const Addr& a) const { return (_v == a._v); }
|
||||||
|
bool operator!=(const Addr& a) const { return (_v != a._v); }
|
||||||
|
bool operator>(const Addr& a) const { return _v > a._v; }
|
||||||
|
bool operator>=(const Addr& a) const { return _v >= a._v; }
|
||||||
|
bool operator<(const Addr& a) const { return _v < a._v; }
|
||||||
|
bool operator<=(const Addr& a) const { return _v <= a._v; }
|
||||||
|
|
||||||
|
Addr operator+(int d) const { return Addr(_v + d); }
|
||||||
|
Addr operator-(int d) const { return Addr(_v - d); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64 _v;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ADDR_H
|
1350
kcachegrind/libcore/cachegrindloader.cpp
Normal file
1350
kcachegrind/libcore/cachegrindloader.cpp
Normal file
File diff suppressed because it is too large
Load diff
101
kcachegrind/libcore/config.cpp
Normal file
101
kcachegrind/libcore/config.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2008 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
// helper functions
|
||||||
|
|
||||||
|
QList<int> toIntList(QStringList l)
|
||||||
|
{
|
||||||
|
QList<int> iList;
|
||||||
|
|
||||||
|
foreach(const QString& s, l)
|
||||||
|
iList << s.toInt();
|
||||||
|
|
||||||
|
return iList;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList toStringList(QList<int> l)
|
||||||
|
{
|
||||||
|
QStringList sList;
|
||||||
|
|
||||||
|
foreach(int i, l)
|
||||||
|
sList << QString::number(i);
|
||||||
|
|
||||||
|
return sList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// ConfigGroup
|
||||||
|
//
|
||||||
|
|
||||||
|
ConfigGroup::ConfigGroup()
|
||||||
|
{}
|
||||||
|
|
||||||
|
ConfigGroup::~ConfigGroup()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void ConfigGroup::setValue(const QString&, const QVariant&, const QVariant&)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QVariant ConfigGroup::value(const QString&, const QVariant& def) const
|
||||||
|
{
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// ConfigStorage
|
||||||
|
//
|
||||||
|
|
||||||
|
ConfigStorage* ConfigStorage::_storage = 0;
|
||||||
|
|
||||||
|
ConfigStorage::ConfigStorage()
|
||||||
|
{
|
||||||
|
_storage = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigStorage::~ConfigStorage()
|
||||||
|
{}
|
||||||
|
|
||||||
|
ConfigGroup* ConfigStorage::group(const QString& group,
|
||||||
|
const QString& optSuffix)
|
||||||
|
{
|
||||||
|
Q_ASSERT(_storage != 0);
|
||||||
|
|
||||||
|
return _storage->getGroup(group, optSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigStorage::setStorage(ConfigStorage* storage)
|
||||||
|
{
|
||||||
|
_storage = storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigStorage::cleanup()
|
||||||
|
{
|
||||||
|
delete _storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigGroup* ConfigStorage::getGroup(const QString&, const QString&)
|
||||||
|
{
|
||||||
|
return new ConfigGroup();
|
||||||
|
}
|
85
kcachegrind/libcore/config.h
Normal file
85
kcachegrind/libcore/config.h
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2008 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
class ConfigStorage;
|
||||||
|
|
||||||
|
// helper functions for storing specific values
|
||||||
|
QList<int> toIntList(QStringList l);
|
||||||
|
QStringList toStringList(QList<int> l);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A group of configuration settings.
|
||||||
|
* Actual implementation for the backends is done in derived classes.
|
||||||
|
*/
|
||||||
|
class ConfigGroup
|
||||||
|
{
|
||||||
|
friend class ConfigStorage;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~ConfigGroup();
|
||||||
|
|
||||||
|
// when value is defaultValue, any previous stored value is removed
|
||||||
|
virtual void setValue(const QString& key, const QVariant& value,
|
||||||
|
const QVariant& defaultValue = QVariant());
|
||||||
|
virtual QVariant value(const QString & key,
|
||||||
|
const QVariant& defaultValue) const;
|
||||||
|
|
||||||
|
// the template version is needed for GUI Qt types
|
||||||
|
template<typename T>
|
||||||
|
inline T value(const QString & key,
|
||||||
|
const QVariant & defaultValue = QVariant()) const
|
||||||
|
{ return qvariant_cast<T>( value(key, defaultValue) ); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ConfigGroup();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an adapter class for different configuration backends.
|
||||||
|
* A singleton.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ConfigStorage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConfigStorage();
|
||||||
|
virtual ~ConfigStorage();
|
||||||
|
|
||||||
|
// if second parameter is not-empty, first search for an existing
|
||||||
|
// group using the optional suffix, and then without.
|
||||||
|
// the group gets readonly.
|
||||||
|
static ConfigGroup* group(const QString& group,
|
||||||
|
const QString& optSuffix = QString());
|
||||||
|
static void setStorage(ConfigStorage*);
|
||||||
|
static void cleanup();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ConfigGroup* getGroup(const QString& group,
|
||||||
|
const QString& optSuffix);
|
||||||
|
|
||||||
|
static ConfigStorage* _storage;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // CONFIG_H
|
141
kcachegrind/libcore/context.cpp
Normal file
141
kcachegrind/libcore/context.cpp
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002 - 2009 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
//---------------------------------------------------
|
||||||
|
// ProfileContext
|
||||||
|
|
||||||
|
ProfileContext* ProfileContext::_contexts = 0;
|
||||||
|
QString* ProfileContext::_typeName = 0;
|
||||||
|
QString* ProfileContext::_i18nTypeName = 0;
|
||||||
|
|
||||||
|
|
||||||
|
ProfileContext::ProfileContext(ProfileContext::Type t)
|
||||||
|
{
|
||||||
|
_type = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileContext* ProfileContext::context(ProfileContext::Type t)
|
||||||
|
{
|
||||||
|
if (!_contexts) {
|
||||||
|
_contexts = new ProfileContext[MaxType+1];
|
||||||
|
for(int i=0;i<=MaxType;i++)
|
||||||
|
_contexts[i] = ProfileContext((ProfileContext::Type)i);
|
||||||
|
}
|
||||||
|
return &(_contexts[t]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileContext::cleanup()
|
||||||
|
{
|
||||||
|
if (_typeName) {
|
||||||
|
delete [] _typeName;
|
||||||
|
_typeName = 0;
|
||||||
|
}
|
||||||
|
if (_i18nTypeName) {
|
||||||
|
delete [] _i18nTypeName;
|
||||||
|
_i18nTypeName = 0;
|
||||||
|
}
|
||||||
|
if (_contexts) {
|
||||||
|
delete [] _contexts;
|
||||||
|
_contexts = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProfileContext::typeName(ProfileContext::Type t)
|
||||||
|
{
|
||||||
|
if (!_typeName) {
|
||||||
|
_typeName = new QString [MaxType+1];
|
||||||
|
QString* strs = _typeName;
|
||||||
|
for(int i=0;i<=MaxType;i++)
|
||||||
|
strs[i] = QString("?");
|
||||||
|
|
||||||
|
strs[InvalidType] = QT_TR_NOOP("Invalid Context");
|
||||||
|
strs[UnknownType] = QT_TR_NOOP("Unknown Context");
|
||||||
|
strs[PartLine] = QT_TR_NOOP("Part Source Line");
|
||||||
|
strs[Line] = QT_TR_NOOP("Source Line");
|
||||||
|
strs[PartLineCall] = QT_TR_NOOP("Part Line Call");
|
||||||
|
strs[LineCall] = QT_TR_NOOP("Line Call");
|
||||||
|
strs[PartLineJump] = QT_TR_NOOP("Part Jump");
|
||||||
|
strs[LineJump] = QT_TR_NOOP("Jump");
|
||||||
|
strs[PartInstr] = QT_TR_NOOP("Part Instruction");
|
||||||
|
strs[Instr] = QT_TR_NOOP("Instruction");
|
||||||
|
strs[PartInstrJump] = QT_TR_NOOP("Part Instruction Jump");
|
||||||
|
strs[InstrJump] = QT_TR_NOOP("Instruction Jump");
|
||||||
|
strs[PartInstrCall] = QT_TR_NOOP("Part Instruction Call");
|
||||||
|
strs[InstrCall] = QT_TR_NOOP("Instruction Call");
|
||||||
|
strs[PartCall] = QT_TR_NOOP("Part Call");
|
||||||
|
strs[Call] = QT_TR_NOOP("Call");
|
||||||
|
strs[PartFunction] = QT_TR_NOOP("Part Function");
|
||||||
|
strs[FunctionSource] = QT_TR_NOOP("Function Source File");
|
||||||
|
strs[Function] = QT_TR_NOOP("Function");
|
||||||
|
strs[FunctionCycle] = QT_TR_NOOP("Function Cycle");
|
||||||
|
strs[PartClass] = QT_TR_NOOP("Part Class");
|
||||||
|
strs[Class] = QT_TR_NOOP("Class");
|
||||||
|
strs[PartFile] = QT_TR_NOOP("Part Source File");
|
||||||
|
strs[File] = QT_TR_NOOP("Source File");
|
||||||
|
strs[PartObject] = QT_TR_NOOP("Part ELF Object");
|
||||||
|
strs[Object] = QT_TR_NOOP("ELF Object");
|
||||||
|
strs[Part] = QT_TR_NOOP("Profile Part");
|
||||||
|
strs[Data] = QT_TR_NOOP("Program Trace");
|
||||||
|
}
|
||||||
|
if (t<0 || t> MaxType) t = MaxType;
|
||||||
|
return _typeName[t];
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileContext::Type ProfileContext::type(const QString& s)
|
||||||
|
{
|
||||||
|
// This is the default context type
|
||||||
|
if (s.isEmpty()) return Function;
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
for (int i=0; i<MaxType;i++) {
|
||||||
|
type = (Type) i;
|
||||||
|
if (typeName(type) == s)
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
return UnknownType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all strings of typeName() are translatable because of QT_TR_NOOP there
|
||||||
|
QString ProfileContext::i18nTypeName(Type t)
|
||||||
|
{
|
||||||
|
if (!_i18nTypeName) {
|
||||||
|
_i18nTypeName = new QString [MaxType+1];
|
||||||
|
for(int i=0;i<=MaxType;i++)
|
||||||
|
_i18nTypeName[i] = QObject::tr(typeName((Type)i).toUtf8());
|
||||||
|
}
|
||||||
|
if (t<0 || t> MaxType) t = MaxType;
|
||||||
|
return _i18nTypeName[t];
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileContext::Type ProfileContext::i18nType(const QString& s)
|
||||||
|
{
|
||||||
|
// This is the default context type
|
||||||
|
if (s.isEmpty()) return Function;
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
for (int i=0; i<MaxType;i++) {
|
||||||
|
type = (Type) i;
|
||||||
|
if (i18nTypeName(type) == s)
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
return UnknownType;
|
||||||
|
}
|
78
kcachegrind/libcore/context.h
Normal file
78
kcachegrind/libcore/context.h
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002 - 2009 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONTEXT_H
|
||||||
|
#define CONTEXT_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for source contexts which event costs contained
|
||||||
|
* in a ProfileData instance, ie. a profiling experiment, can relate to.
|
||||||
|
*
|
||||||
|
* This only includes code/source related context. Any other cost
|
||||||
|
* context such as thread number, see DataContext, which relates to ProfileData.
|
||||||
|
*/
|
||||||
|
class ProfileContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// RTTI for trace item classes, using type() method
|
||||||
|
enum Type {
|
||||||
|
InvalidType = 0, UnknownType,
|
||||||
|
PartInstr, Instr,
|
||||||
|
PartLine, Line,
|
||||||
|
PartInstrJump, InstrJump,
|
||||||
|
PartLineJump, LineJump,
|
||||||
|
PartInstrCall, InstrCall,
|
||||||
|
PartLineCall, LineCall,
|
||||||
|
PartCall, Call,
|
||||||
|
PartLineRegion, LineRegion,
|
||||||
|
PartFunction, FunctionSource, Function, FunctionCycle,
|
||||||
|
PartClass, Class, ClassCycle,
|
||||||
|
PartFile, File, FileCycle,
|
||||||
|
PartObject, Object, ObjectCycle,
|
||||||
|
Part, Data,
|
||||||
|
MaxType };
|
||||||
|
|
||||||
|
ProfileContext(ProfileContext::Type = InvalidType);
|
||||||
|
|
||||||
|
ProfileContext::Type type() { return _type; }
|
||||||
|
|
||||||
|
static ProfileContext* context(ProfileContext::Type);
|
||||||
|
|
||||||
|
// conversion of context type to locale independent string (e.g. for config)
|
||||||
|
static QString typeName(Type);
|
||||||
|
static Type type(const QString&);
|
||||||
|
// the versions below should be used for user visible strings, as
|
||||||
|
// these use localization settings
|
||||||
|
static QString i18nTypeName(Type);
|
||||||
|
static Type i18nType(const QString&);
|
||||||
|
|
||||||
|
// clean up some static data
|
||||||
|
static void cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type _type;
|
||||||
|
|
||||||
|
static ProfileContext* _contexts;
|
||||||
|
static QString* _typeName;
|
||||||
|
static QString* _i18nTypeName;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CONTEXT_H
|
618
kcachegrind/libcore/costitem.cpp
Normal file
618
kcachegrind/libcore/costitem.cpp
Normal file
|
@ -0,0 +1,618 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002 - 2009 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "costitem.h"
|
||||||
|
|
||||||
|
#include "tracedata.h"
|
||||||
|
|
||||||
|
#define TRACE_DEBUG 0
|
||||||
|
#define TRACE_ASSERTIONS 0
|
||||||
|
|
||||||
|
//---------------------------------------------------
|
||||||
|
// ProfileCost
|
||||||
|
|
||||||
|
CostItem::CostItem(ProfileContext* c)
|
||||||
|
{
|
||||||
|
_position = 0;
|
||||||
|
_dep = 0;
|
||||||
|
_dirty = true;
|
||||||
|
|
||||||
|
_context = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
CostItem::~CostItem()
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
void CostItem::clear()
|
||||||
|
{
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString CostItem::costString(EventTypeSet*)
|
||||||
|
{
|
||||||
|
return QString("(no cost)");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CostItem::name() const
|
||||||
|
{
|
||||||
|
if (part()) {
|
||||||
|
return QObject::tr("%1 from %2").arg(_dep->name()).arg(part()->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_dep)
|
||||||
|
return _dep->name();
|
||||||
|
|
||||||
|
return QObject::tr("(unknown)");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CostItem::prettyName() const
|
||||||
|
{
|
||||||
|
if (name().isEmpty()) return QObject::tr("(unknown)");
|
||||||
|
return name();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CostItem::formattedName() const
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CostItem::fullName() const
|
||||||
|
{
|
||||||
|
return QString("%1 %2")
|
||||||
|
.arg(ProfileContext::typeName(type())).arg(prettyName());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CostItem::toString()
|
||||||
|
{
|
||||||
|
return QString("%1\n [%3]").arg(fullName()).arg(costString(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CostItem::invalidate()
|
||||||
|
{
|
||||||
|
if (_dirty) return;
|
||||||
|
_dirty = true;
|
||||||
|
|
||||||
|
if (_dep)
|
||||||
|
_dep->invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CostItem::update()
|
||||||
|
{
|
||||||
|
_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TracePart* CostItem::part()
|
||||||
|
{
|
||||||
|
return _position ? _position->part() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TracePart* CostItem::part() const
|
||||||
|
{
|
||||||
|
return _position ? _position->part() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceData* CostItem::data()
|
||||||
|
{
|
||||||
|
return _position ? _position->data() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TraceData* CostItem::data() const
|
||||||
|
{
|
||||||
|
return _position ? _position->data() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------
|
||||||
|
// ProfileCostArray
|
||||||
|
|
||||||
|
|
||||||
|
const int ProfileCostArray::MaxRealIndex = MaxRealIndexValue;
|
||||||
|
const int ProfileCostArray::InvalidIndex = -1;
|
||||||
|
|
||||||
|
|
||||||
|
ProfileCostArray::ProfileCostArray(ProfileContext* context)
|
||||||
|
: CostItem(context)
|
||||||
|
{
|
||||||
|
_cachedType = 0; // no virtual value cached
|
||||||
|
_allocCount = 0;
|
||||||
|
_count = 0;
|
||||||
|
_cost = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileCostArray::ProfileCostArray()
|
||||||
|
: CostItem(ProfileContext::context(ProfileContext::UnknownType))
|
||||||
|
{
|
||||||
|
_cachedType = 0; // no virtual value cached
|
||||||
|
_allocCount = 0;
|
||||||
|
_count = 0;
|
||||||
|
_cost = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileCostArray::~ProfileCostArray()
|
||||||
|
{
|
||||||
|
if (_cost) delete[] _cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ProfileCostArray::clear()
|
||||||
|
{
|
||||||
|
_count = 0;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileCostArray::reserve(int count)
|
||||||
|
{
|
||||||
|
if (count <= _allocCount) return;
|
||||||
|
|
||||||
|
SubCost* newcost = new SubCost[count];
|
||||||
|
if (_cost) {
|
||||||
|
/* first _count values are valid and have to be preserved */
|
||||||
|
for(int i=0; i<_count; i++)
|
||||||
|
newcost[i] = _cost[i];
|
||||||
|
delete[] _cost;
|
||||||
|
}
|
||||||
|
_cost = newcost;
|
||||||
|
_allocCount = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileCostArray::set(EventTypeMapping* mapping, const char* s)
|
||||||
|
{
|
||||||
|
if (!mapping) return;
|
||||||
|
if (!s) {
|
||||||
|
clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reserve(mapping->set()->realCount());
|
||||||
|
|
||||||
|
while(*s == ' ') s++;
|
||||||
|
|
||||||
|
if (mapping->isIdentity()) {
|
||||||
|
int i = 0;
|
||||||
|
while(i<mapping->count()) {
|
||||||
|
if (!_cost[i].set(&s)) break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
_count = i;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int i = 0, maxIndex = 0, index;
|
||||||
|
while(1) {
|
||||||
|
index = mapping->realIndex(i);
|
||||||
|
if (maxIndex<index) maxIndex=index;
|
||||||
|
if (index == ProfileCostArray::InvalidIndex) break;
|
||||||
|
if (!_cost[index].set(&s)) break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// we have to set all costs of unused indexes till maxIndex to zero
|
||||||
|
for(i=mapping->firstUnused(); i<=maxIndex; i=mapping->nextUnused(i))
|
||||||
|
_cost[i] = 0;
|
||||||
|
_count = maxIndex;
|
||||||
|
}
|
||||||
|
Q_ASSERT(_count <= _allocCount);
|
||||||
|
// a cost change has to be propagated (esp. in subclasses)
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileCostArray::set(EventTypeMapping* mapping, FixString & s)
|
||||||
|
{
|
||||||
|
if (!mapping) return;
|
||||||
|
s.stripSpaces();
|
||||||
|
if (s.isEmpty()) {
|
||||||
|
clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reserve(mapping->set()->realCount());
|
||||||
|
|
||||||
|
if (mapping->isIdentity()) {
|
||||||
|
int i = 0;
|
||||||
|
while(i<mapping->count()) {
|
||||||
|
if (!s.stripUInt64(_cost[i])) break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
_count = i;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int i = 0, maxIndex = 0, index;
|
||||||
|
while(1) {
|
||||||
|
index = mapping->realIndex(i);
|
||||||
|
if (maxIndex<index) maxIndex=index;
|
||||||
|
if (index == ProfileCostArray::InvalidIndex) break;
|
||||||
|
if (!s.stripUInt64(_cost[index])) break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// we have to set all costs of unused indexes till maxIndex to zero
|
||||||
|
for(i=mapping->firstUnused(); i<=maxIndex; i=mapping->nextUnused(i))
|
||||||
|
_cost[i] = 0;
|
||||||
|
_count = maxIndex+1;
|
||||||
|
}
|
||||||
|
Q_ASSERT(_count <= _allocCount);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ProfileCostArray::addCost(EventTypeMapping* mapping, const char* s)
|
||||||
|
{
|
||||||
|
if (!mapping || !s) return;
|
||||||
|
reserve(mapping->set()->realCount());
|
||||||
|
|
||||||
|
SubCost v;
|
||||||
|
if (mapping->isIdentity()) {
|
||||||
|
int i = 0;
|
||||||
|
while(i<mapping->count()) {
|
||||||
|
if (!v.set(&s)) break;
|
||||||
|
if (i<_count)
|
||||||
|
_cost[i] += v;
|
||||||
|
else
|
||||||
|
_cost[i] = v;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i > _count) _count = i;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int i = 0, maxIndex = 0, index;
|
||||||
|
while(1) {
|
||||||
|
if (!v.set(&s)) break;
|
||||||
|
index = mapping->realIndex(i);
|
||||||
|
if (maxIndex<index) maxIndex=index;
|
||||||
|
if (index == ProfileCostArray::InvalidIndex) break;
|
||||||
|
if (index<_count)
|
||||||
|
_cost[index] += v;
|
||||||
|
else
|
||||||
|
_cost[index] = v;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (maxIndex >= _count) {
|
||||||
|
/* we have to set all costs of unused indexes in the interval
|
||||||
|
* [_count;maxIndex] to zero */
|
||||||
|
for(i=mapping->nextUnused(_count-1); i<=maxIndex; i=mapping->nextUnused(i))
|
||||||
|
_cost[i] = 0;
|
||||||
|
_count = maxIndex+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(_count <= _allocCount);
|
||||||
|
invalidate();
|
||||||
|
|
||||||
|
#if TRACE_DEBUG
|
||||||
|
_dirty = false; // do not recurse !
|
||||||
|
qDebug("%s\n now %s", qPrintable( fullName() ),
|
||||||
|
qPrintable( ProfileCostArray::costString(0) ));
|
||||||
|
_dirty = true; // because of invalidate()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileCostArray::addCost(EventTypeMapping* mapping, FixString & s)
|
||||||
|
{
|
||||||
|
if (!mapping) return;
|
||||||
|
s.stripSpaces();
|
||||||
|
if (s.isEmpty()) return;
|
||||||
|
reserve(mapping->set()->realCount());
|
||||||
|
|
||||||
|
SubCost v;
|
||||||
|
if (mapping->isIdentity()) {
|
||||||
|
int i = 0;
|
||||||
|
while(i<mapping->count()) {
|
||||||
|
if (!s.stripUInt64(v)) break;
|
||||||
|
if (i<_count)
|
||||||
|
_cost[i] += v;
|
||||||
|
else
|
||||||
|
_cost[i] = v;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i > _count) _count = i;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int i = 0, maxIndex = 0, index;
|
||||||
|
while(1) {
|
||||||
|
if (!s.stripUInt64(v)) break;
|
||||||
|
index = mapping->realIndex(i);
|
||||||
|
if (maxIndex<index) maxIndex=index;
|
||||||
|
if (index == ProfileCostArray::InvalidIndex) break;
|
||||||
|
if (index<_count)
|
||||||
|
_cost[index] += v;
|
||||||
|
else
|
||||||
|
_cost[index] = v;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (maxIndex >= _count) {
|
||||||
|
/* we have to set all costs of unused indexes in the interval
|
||||||
|
* [_count;maxIndex] to zero */
|
||||||
|
for(i=mapping->nextUnused(_count-1); i<=maxIndex; i=mapping->nextUnused(i))
|
||||||
|
_cost[i] = 0;
|
||||||
|
_count = maxIndex+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(_count <= _allocCount);
|
||||||
|
invalidate();
|
||||||
|
|
||||||
|
#if TRACE_DEBUG
|
||||||
|
_dirty = false; // do not recurse !
|
||||||
|
qDebug("%s\n now %s", qPrintable( fullName() ),
|
||||||
|
qPrintable( ProfileCostArray::costString(0 ) ) );
|
||||||
|
_dirty = true; // because of invalidate()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// update each subcost to be maximum of old and given costs
|
||||||
|
void ProfileCostArray::maxCost(EventTypeMapping* mapping, FixString & s)
|
||||||
|
{
|
||||||
|
if (!mapping) return;
|
||||||
|
s.stripSpaces();
|
||||||
|
if (s.isEmpty()) return;
|
||||||
|
reserve(mapping->set()->realCount());
|
||||||
|
|
||||||
|
SubCost v;
|
||||||
|
if (mapping->isIdentity()) {
|
||||||
|
int i = 0;
|
||||||
|
while(i<mapping->count()) {
|
||||||
|
if (!s.stripUInt64(v)) break;
|
||||||
|
if (i<_count) {
|
||||||
|
if (v>_cost[i]) _cost[i] = v;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_cost[i] = v;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i > _count) _count = i;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int i = 0, maxIndex = 0, index;
|
||||||
|
while(1) {
|
||||||
|
if (!s.stripUInt64(v)) break;
|
||||||
|
index = mapping->realIndex(i);
|
||||||
|
if (maxIndex<index) maxIndex=index;
|
||||||
|
if (index == ProfileCostArray::InvalidIndex) break;
|
||||||
|
if (index<_count) {
|
||||||
|
if (v>_cost[index]) _cost[index] = v;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_cost[index] = v;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (maxIndex >= _count) {
|
||||||
|
/* we have to set all costs of unused indexes in the interval
|
||||||
|
* [_count;maxIndex] to zero */
|
||||||
|
for(i=mapping->nextUnused(_count-1); i<=maxIndex; i=mapping->nextUnused(i))
|
||||||
|
_cost[i] = 0;
|
||||||
|
_count = maxIndex+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(_count <= _allocCount);
|
||||||
|
invalidate();
|
||||||
|
|
||||||
|
#if TRACE_DEBUG
|
||||||
|
_dirty = false; // do not recurse !
|
||||||
|
qDebug("%s\n now %s", qPrintable( fullName() ),
|
||||||
|
qPrintable(ProfileCostArray::costString(0)));
|
||||||
|
_dirty = true; // because of invalidate()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ProfileCostArray::addCost(ProfileCostArray* item)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
// we have to update the other item if needed
|
||||||
|
// because we access the item costs directly
|
||||||
|
if (item->_dirty) item->update();
|
||||||
|
|
||||||
|
// make sure we have enough space allocated
|
||||||
|
reserve(item->_count);
|
||||||
|
|
||||||
|
if (item->_count < _count) {
|
||||||
|
for (i = 0; i<item->_count; i++)
|
||||||
|
_cost[i] += item->_cost[i];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (i = 0; i<_count; i++)
|
||||||
|
_cost[i] += item->_cost[i];
|
||||||
|
for (; i<item->_count; i++)
|
||||||
|
_cost[i] = item->_cost[i];
|
||||||
|
_count = item->_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(_count <= _allocCount);
|
||||||
|
invalidate();
|
||||||
|
|
||||||
|
#if TRACE_DEBUG
|
||||||
|
_dirty = false; // do not recurse !
|
||||||
|
qDebug("%s added cost item\n %s\n now %s",
|
||||||
|
qPrintable( fullName() ), qPrintable(item->fullName()),
|
||||||
|
qPrintable(ProfileCostArray::costString(0)));
|
||||||
|
_dirty = true; // because of invalidate()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileCostArray::maxCost(ProfileCostArray* item)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
// we have to update the other item if needed
|
||||||
|
// because we access the item costs directly
|
||||||
|
if (item->_dirty) item->update();
|
||||||
|
|
||||||
|
// make sure we have enough space allocated
|
||||||
|
reserve(item->_count);
|
||||||
|
|
||||||
|
if (item->_count < _count) {
|
||||||
|
for (i = 0; i<item->_count; i++)
|
||||||
|
if (_cost[i] < item->_cost[i]) _cost[i] = item->_cost[i];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (i = 0; i<_count; i++)
|
||||||
|
if (_cost[i] < item->_cost[i]) _cost[i] = item->_cost[i];
|
||||||
|
for (; i<item->_count; i++)
|
||||||
|
_cost[i] = item->_cost[i];
|
||||||
|
_count = item->_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(_count <= _allocCount);
|
||||||
|
invalidate();
|
||||||
|
|
||||||
|
#if TRACE_DEBUG
|
||||||
|
_dirty = false; // do not recurse !
|
||||||
|
qDebug("%s added cost item\n %s\n now %s",
|
||||||
|
qPrintable( fullName() ), qPrintable(item->fullName()),
|
||||||
|
qPrintable( ProfileCostArray::costString(0) ));
|
||||||
|
_dirty = true; // because of invalidate()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileCostArray::addCost(int realIndex, SubCost value)
|
||||||
|
{
|
||||||
|
if (realIndex<0 || realIndex>=MaxRealIndex) return;
|
||||||
|
if (value == 0) return;
|
||||||
|
|
||||||
|
reserve(realIndex+1);
|
||||||
|
if (realIndex < _count)
|
||||||
|
_cost[realIndex] += value;
|
||||||
|
else {
|
||||||
|
for(int i=_count;i<realIndex;i++)
|
||||||
|
_cost[i] = 0;
|
||||||
|
_cost[realIndex] = value;
|
||||||
|
_count = realIndex+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(_count <= _allocCount);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileCostArray::maxCost(int realIndex, SubCost value)
|
||||||
|
{
|
||||||
|
if (realIndex<0 || realIndex>=MaxRealIndex) return;
|
||||||
|
|
||||||
|
reserve(realIndex+1);
|
||||||
|
if (realIndex<_count) {
|
||||||
|
if (value>_cost[realIndex]) _cost[realIndex] = value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for(int i=_count;i<realIndex;i++)
|
||||||
|
_cost[i] = 0;
|
||||||
|
_cost[realIndex] = value;
|
||||||
|
_count = realIndex+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(_count <= _allocCount);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ProfileCostArray ProfileCostArray::diff(ProfileCostArray* item)
|
||||||
|
{
|
||||||
|
ProfileCostArray res(context());
|
||||||
|
|
||||||
|
// we have to update the other item if needed
|
||||||
|
// because we access the item costs directly
|
||||||
|
if (item->_dirty) item->update();
|
||||||
|
|
||||||
|
int maxCount = (item->_count > _count) ? item->_count : _count;
|
||||||
|
|
||||||
|
res.reserve(maxCount);
|
||||||
|
for (int i=0; i<maxCount;i++)
|
||||||
|
res._cost[i] = item->subCost(i) - subCost(i);
|
||||||
|
res._count = maxCount;
|
||||||
|
|
||||||
|
Q_ASSERT(res._count <= res._allocCount);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProfileCostArray::costString(EventTypeSet* set)
|
||||||
|
{
|
||||||
|
QString res;
|
||||||
|
|
||||||
|
if (_dirty) update();
|
||||||
|
|
||||||
|
int maxIndex = set ? set->realCount() : ProfileCostArray::MaxRealIndex;
|
||||||
|
for (int i = 0; i<maxIndex; i++) {
|
||||||
|
if (!res.isEmpty()) res += ", ";
|
||||||
|
if (set) res += set->type(i)->name() + ' ';
|
||||||
|
|
||||||
|
res += subCost(i).pretty();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ProfileCostArray::invalidate()
|
||||||
|
{
|
||||||
|
if (_dirty) return;
|
||||||
|
_dirty = true;
|
||||||
|
_cachedType = 0; // cached value is invalid, too
|
||||||
|
|
||||||
|
if (_dep)
|
||||||
|
_dep->invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileCostArray::update()
|
||||||
|
{
|
||||||
|
_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is only for real types
|
||||||
|
SubCost ProfileCostArray::subCost(int idx)
|
||||||
|
{
|
||||||
|
if (idx<0) return 0;
|
||||||
|
|
||||||
|
/* update if needed as cost could be calculated dynamically in subclasses
|
||||||
|
* this can change _count !! */
|
||||||
|
if (_dirty) update();
|
||||||
|
if (idx>=_count) return 0;
|
||||||
|
|
||||||
|
return _cost[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SubCost ProfileCostArray::subCost(EventType* t)
|
||||||
|
{
|
||||||
|
if (!t) return 0;
|
||||||
|
if (_cachedType != t) {
|
||||||
|
_cachedType = t;
|
||||||
|
_cachedCost = t->subCost(this);
|
||||||
|
}
|
||||||
|
return _cachedCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProfileCostArray::prettySubCost(EventType* t)
|
||||||
|
{
|
||||||
|
return subCost(t).pretty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProfileCostArray::prettySubCostPerCall(EventType* t, uint64 calls)
|
||||||
|
{
|
||||||
|
if (calls == 0) {
|
||||||
|
/* For callgrind, a call count of zero means that
|
||||||
|
* a function was already active when measuring started
|
||||||
|
* (even without that possibility, we never should crash).
|
||||||
|
* To show full cost, set <calls> to 1.
|
||||||
|
*/
|
||||||
|
calls = 1;
|
||||||
|
}
|
||||||
|
return SubCost(subCost(t) / calls).pretty();
|
||||||
|
}
|
||||||
|
|
212
kcachegrind/libcore/costitem.h
Normal file
212
kcachegrind/libcore/costitem.h
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002 - 2009 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COST_H
|
||||||
|
#define COST_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "subcost.h"
|
||||||
|
#include "context.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
class EventType;
|
||||||
|
class EventTypeSet;
|
||||||
|
class EventTypeMapping;
|
||||||
|
class TracePart;
|
||||||
|
class TraceData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for cost items.
|
||||||
|
*/
|
||||||
|
class CostItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
CostItem(ProfileContext*);
|
||||||
|
virtual ~CostItem();
|
||||||
|
|
||||||
|
ProfileContext* context() const { return _context; }
|
||||||
|
ProfileContext::Type type() const { return context()->type(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns dynamic name info (without type)
|
||||||
|
*/
|
||||||
|
virtual QString name() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to name, but prettyfied = more descriptive to humans
|
||||||
|
*/
|
||||||
|
virtual QString prettyName() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A HTMLified version of name, can return empty string
|
||||||
|
*/
|
||||||
|
virtual QString formattedName() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns text of all cost metrics
|
||||||
|
*/
|
||||||
|
virtual QString costString(EventTypeSet*);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns type name + dynamic name
|
||||||
|
*/
|
||||||
|
QString fullName() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns full name + cost text
|
||||||
|
*/
|
||||||
|
QString toString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all cost counters to zero
|
||||||
|
*/
|
||||||
|
virtual void clear();
|
||||||
|
|
||||||
|
/** Invalidate the cost attributes.
|
||||||
|
* An invalidated object needs to be recalculated when a cost
|
||||||
|
* attribute is requested (e.g. by subCost()).
|
||||||
|
* Has to be overwritten by subclasses when the cost influences costs of
|
||||||
|
* other cost items. If only one item depends on the cost of this item,
|
||||||
|
* it can by set with setDependant() without a need for overwriting.
|
||||||
|
*/
|
||||||
|
virtual void invalidate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a dependant to be invalidated when this cost is invalidated.
|
||||||
|
* Call this function directly after the constructor.
|
||||||
|
*/
|
||||||
|
void setDependant(CostItem* d) { _dep = d; }
|
||||||
|
|
||||||
|
CostItem* dependant() { return _dep; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this item is from a single profile data file, position
|
||||||
|
* points to a TracePart, otherwise to a TraceData object.
|
||||||
|
*/
|
||||||
|
void setPosition(CostItem* p) { _position = p; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redefine the context after construction
|
||||||
|
*/
|
||||||
|
void setContext(ProfileContext* context) { _context = context; }
|
||||||
|
|
||||||
|
// getters for specific positions, to be overwritten
|
||||||
|
virtual TracePart* part();
|
||||||
|
virtual const TracePart* part() const;
|
||||||
|
virtual TraceData* data();
|
||||||
|
virtual const TraceData* data() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** Updates cost attributes.
|
||||||
|
* This has to be called by subclasses that access cost attributes
|
||||||
|
* directly
|
||||||
|
*/
|
||||||
|
virtual void update();
|
||||||
|
|
||||||
|
ProfileContext* _context;
|
||||||
|
bool _dirty;
|
||||||
|
|
||||||
|
CostItem* _position;
|
||||||
|
CostItem* _dep;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// The maximal number of subcosts in a ProfileCostArray and
|
||||||
|
// event types in a EventSet. Does not really matter for memory
|
||||||
|
// consumption as cost for a ProfileCostArray is dynamically
|
||||||
|
// allocated depending on used number of event types, and there
|
||||||
|
// will be only a low number of objects for EventType{,Set,Mapping}.
|
||||||
|
#define MaxRealIndexValue 200
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of basic cost metrics for a trace item.
|
||||||
|
*
|
||||||
|
* The semantic of specific indexes is stored in the
|
||||||
|
* EventTypeSet of the TraceData object holding this ProfileCostArray.
|
||||||
|
*/
|
||||||
|
class ProfileCostArray: public CostItem
|
||||||
|
{
|
||||||
|
friend class EventType;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
static const int MaxRealIndex;
|
||||||
|
static const int InvalidIndex;
|
||||||
|
|
||||||
|
|
||||||
|
ProfileCostArray(ProfileContext*);
|
||||||
|
ProfileCostArray();
|
||||||
|
virtual ~ProfileCostArray();
|
||||||
|
|
||||||
|
virtual QString costString(EventTypeSet*);
|
||||||
|
|
||||||
|
virtual void clear();
|
||||||
|
|
||||||
|
// reserve space for cost
|
||||||
|
void reserve(int);
|
||||||
|
|
||||||
|
// set costs according to the mapping order of event types
|
||||||
|
void set(EventTypeMapping*, const char*);
|
||||||
|
void set(EventTypeMapping*, FixString&);
|
||||||
|
// add costs according to the mapping order of event types
|
||||||
|
void addCost(EventTypeMapping*, const char*);
|
||||||
|
void addCost(EventTypeMapping*, FixString&);
|
||||||
|
// add the cost of another item
|
||||||
|
void addCost(ProfileCostArray* item);
|
||||||
|
void addCost(int index, SubCost value);
|
||||||
|
|
||||||
|
// maximal cost
|
||||||
|
void maxCost(EventTypeMapping*, FixString&);
|
||||||
|
void maxCost(ProfileCostArray* item);
|
||||||
|
void maxCost(int index, SubCost value);
|
||||||
|
ProfileCostArray diff(ProfileCostArray* item);
|
||||||
|
|
||||||
|
virtual void invalidate();
|
||||||
|
|
||||||
|
/** Returns a sub cost. This automatically triggers
|
||||||
|
* a call to update() if needed.
|
||||||
|
*/
|
||||||
|
SubCost subCost(EventType*);
|
||||||
|
|
||||||
|
/** Returns a cost attribute converted to a string
|
||||||
|
* (with space after every 3 digits)
|
||||||
|
*/
|
||||||
|
QString prettySubCost(EventType*);
|
||||||
|
|
||||||
|
QString prettySubCostPerCall(EventType* t, uint64 calls);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Only used by friend class EventType: return subcost by index
|
||||||
|
SubCost subCost(int);
|
||||||
|
|
||||||
|
SubCost* _cost;
|
||||||
|
int _count; // only _count first indexes of _cost are used
|
||||||
|
int _allocCount; // number of allocated subcost entries
|
||||||
|
|
||||||
|
// cache last virtual subcost for faster access
|
||||||
|
SubCost _cachedCost;
|
||||||
|
EventType* _cachedType;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // COST_H
|
319
kcachegrind/libcore/coverage.cpp
Normal file
319
kcachegrind/libcore/coverage.cpp
Normal file
|
@ -0,0 +1,319 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function Coverage Analysis
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "coverage.h"
|
||||||
|
|
||||||
|
//#define DEBUG_COVERAGE 1
|
||||||
|
|
||||||
|
EventType* Coverage::_costType;
|
||||||
|
|
||||||
|
const int Coverage::maxHistogramDepth = maxHistogramDepthValue;
|
||||||
|
const int Coverage::Rtti = 1;
|
||||||
|
|
||||||
|
Coverage::Coverage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Coverage::init()
|
||||||
|
{
|
||||||
|
_self = 0.0;
|
||||||
|
_incl = 0.0;
|
||||||
|
_callCount = 0.0;
|
||||||
|
// should always be overwritten before usage
|
||||||
|
_firstPercentage = 1.0;
|
||||||
|
_minDistance = 9999;
|
||||||
|
_maxDistance = 0;
|
||||||
|
_active = false;
|
||||||
|
_inRecursion = false;
|
||||||
|
for (int i = 0;i<maxHistogramDepth;i++) {
|
||||||
|
_selfHisto[i] = 0.0;
|
||||||
|
_inclHisto[i] = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Coverage::inclusiveMedian()
|
||||||
|
{
|
||||||
|
double maxP = _inclHisto[0];
|
||||||
|
int medD = 0;
|
||||||
|
for (int i = 1;i<maxHistogramDepth;i++)
|
||||||
|
if (_inclHisto[i]>maxP) {
|
||||||
|
maxP = _inclHisto[i];
|
||||||
|
medD = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return medD;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Coverage::selfMedian()
|
||||||
|
{
|
||||||
|
double maxP = _selfHisto[0];
|
||||||
|
int medD = 0;
|
||||||
|
for (int i = 1;i<maxHistogramDepth;i++)
|
||||||
|
if (_selfHisto[i]>maxP) {
|
||||||
|
maxP = _selfHisto[i];
|
||||||
|
medD = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return medD;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceFunctionList Coverage::coverage(TraceFunction* f, CoverageMode m,
|
||||||
|
EventType* ct)
|
||||||
|
{
|
||||||
|
invalidate(f->data(), Coverage::Rtti);
|
||||||
|
|
||||||
|
_costType = ct;
|
||||||
|
|
||||||
|
// function f takes ownership over c!
|
||||||
|
Coverage* c = new Coverage();
|
||||||
|
c->setFunction(f);
|
||||||
|
c->init();
|
||||||
|
|
||||||
|
TraceFunctionList l;
|
||||||
|
|
||||||
|
if (m == Caller)
|
||||||
|
c->addCallerCoverage(l, 1.0, 0);
|
||||||
|
else
|
||||||
|
c->addCallingCoverage(l, 1.0, 1.0, 0);
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Coverage::addCallerCoverage(TraceFunctionList& fList,
|
||||||
|
double pBack, int d)
|
||||||
|
{
|
||||||
|
if (_inRecursion) return;
|
||||||
|
|
||||||
|
double incl;
|
||||||
|
incl = (double) (_function->inclusive()->subCost(_costType));
|
||||||
|
|
||||||
|
if (_active) {
|
||||||
|
#ifdef DEBUG_COVERAGE
|
||||||
|
qDebug("CallerCov: D %d, %s (was active, incl %f, self %f): newP %f", d,
|
||||||
|
qPrintable(_function->prettyName()), _incl, _self, pBack);
|
||||||
|
#endif
|
||||||
|
_inRecursion = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_active = true;
|
||||||
|
|
||||||
|
// only add cost if this is no recursion
|
||||||
|
|
||||||
|
_incl += pBack;
|
||||||
|
_firstPercentage = pBack;
|
||||||
|
|
||||||
|
if (_minDistance > d) _minDistance = d;
|
||||||
|
if (_maxDistance < d) _maxDistance = d;
|
||||||
|
if (d<maxHistogramDepth) {
|
||||||
|
_inclHisto[d] += pBack;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_inclHisto[maxHistogramDepth-1] += pBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_COVERAGE
|
||||||
|
qDebug("CallerCov: D %d, %s (now active, new incl %f): newP %f",
|
||||||
|
d, qPrintable(_function->prettyName()), _incl, pBack);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
double callVal, pBackNew;
|
||||||
|
|
||||||
|
foreach(TraceCall* call, _function->callers()) {
|
||||||
|
if (call->inCycle()>0) continue;
|
||||||
|
if (call->isRecursion()) continue;
|
||||||
|
|
||||||
|
if (call->subCost(_costType)>0) {
|
||||||
|
TraceFunction* caller = call->caller();
|
||||||
|
|
||||||
|
Coverage* c = (Coverage*) caller->association(rtti());
|
||||||
|
if (!c) {
|
||||||
|
c = new Coverage();
|
||||||
|
c->setFunction(caller);
|
||||||
|
}
|
||||||
|
if (!c->isValid()) {
|
||||||
|
c->init();
|
||||||
|
fList.append(caller);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->isActive()) continue;
|
||||||
|
if (c->inRecursion()) continue;
|
||||||
|
|
||||||
|
callVal = (double) call->subCost(_costType);
|
||||||
|
pBackNew = pBack * (callVal / incl);
|
||||||
|
|
||||||
|
// FIXME ?!?
|
||||||
|
|
||||||
|
if (!c->isActive()) {
|
||||||
|
if (d>=0)
|
||||||
|
c->callCount() += (double)call->callCount();
|
||||||
|
else
|
||||||
|
c->callCount() += _callCount;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// adjust pNew by sum of geometric series of recursion factor.
|
||||||
|
// Thus we can avoid endless recursion here
|
||||||
|
pBackNew *= 1.0 / (1.0 - pBackNew / c->firstPercentage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit depth
|
||||||
|
if (pBackNew > 0.0001)
|
||||||
|
c->addCallerCoverage(fList, pBackNew, d+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_inRecursion)
|
||||||
|
_inRecursion = false;
|
||||||
|
else if (_active)
|
||||||
|
_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pForward is time on percent used,
|
||||||
|
* pBack is given to allow for calculation of call counts
|
||||||
|
*/
|
||||||
|
void Coverage::addCallingCoverage(TraceFunctionList& fList,
|
||||||
|
double pForward, double pBack, int d)
|
||||||
|
{
|
||||||
|
if (_inRecursion) return;
|
||||||
|
|
||||||
|
#ifdef DEBUG_COVERAGE
|
||||||
|
static const char* spaces = " ";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
double self, incl;
|
||||||
|
incl = (double) (_function->inclusive()->subCost(_costType));
|
||||||
|
|
||||||
|
#ifdef DEBUG_COVERAGE
|
||||||
|
qDebug("CngCov:%s - %s (incl %f, self %f): forward %f, back %f",
|
||||||
|
spaces+strlen(spaces)-d,
|
||||||
|
qPrintable(_function->prettyName()), _incl, _self, pForward, pBack);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
if (_active) {
|
||||||
|
_inRecursion = true;
|
||||||
|
|
||||||
|
#ifdef DEBUG_COVERAGE
|
||||||
|
qDebug("CngCov:%s < %s: STOP (is active)",
|
||||||
|
spaces+strlen(spaces)-d,
|
||||||
|
qPrintable(_function->prettyName()));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_active = true;
|
||||||
|
|
||||||
|
// only add cost if this is no recursion
|
||||||
|
self = pForward * (_function->subCost(_costType)) / incl;
|
||||||
|
_incl += pForward;
|
||||||
|
_self += self;
|
||||||
|
_firstPercentage = pForward;
|
||||||
|
|
||||||
|
if (_minDistance > d) _minDistance = d;
|
||||||
|
if (_maxDistance < d) _maxDistance = d;
|
||||||
|
if (d<maxHistogramDepth) {
|
||||||
|
_inclHisto[d] += pForward;
|
||||||
|
_selfHisto[d] += self;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_inclHisto[maxHistogramDepth-1] += pForward;
|
||||||
|
_selfHisto[maxHistogramDepth-1] += self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_COVERAGE
|
||||||
|
qDebug("CngCov:%s < %s (incl %f, self %f)",
|
||||||
|
spaces+strlen(spaces)-d,
|
||||||
|
qPrintable(_function->prettyName()), _incl, _self);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
double callVal, pForwardNew, pBackNew;
|
||||||
|
|
||||||
|
foreach(TraceCall* call, _function->callings()) {
|
||||||
|
if (call->inCycle()>0) continue;
|
||||||
|
if (call->isRecursion()) continue;
|
||||||
|
|
||||||
|
if (call->subCost(_costType)>0) {
|
||||||
|
TraceFunction* calling = call->called();
|
||||||
|
|
||||||
|
Coverage* c = (Coverage*) calling->association(rtti());
|
||||||
|
if (!c) {
|
||||||
|
c = new Coverage();
|
||||||
|
c->setFunction(calling);
|
||||||
|
}
|
||||||
|
if (!c->isValid()) {
|
||||||
|
c->init();
|
||||||
|
fList.append(calling);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->isActive()) continue;
|
||||||
|
if (c->inRecursion()) continue;
|
||||||
|
|
||||||
|
callVal = (double) call->subCost(_costType);
|
||||||
|
pForwardNew = pForward * (callVal / incl);
|
||||||
|
pBackNew = pBack * (callVal /
|
||||||
|
calling->inclusive()->subCost(_costType));
|
||||||
|
|
||||||
|
if (!c->isActive()) {
|
||||||
|
c->callCount() += pBack * call->callCount();
|
||||||
|
|
||||||
|
#ifdef DEBUG_COVERAGE
|
||||||
|
qDebug("CngCov:%s > %s: forward %f, back %f, calls %f -> %f, now %f",
|
||||||
|
spaces+strlen(spaces)-d,
|
||||||
|
qPrintable(calling->prettyName()),
|
||||||
|
pForwardNew, pBackNew,
|
||||||
|
(double)call->callCount(),
|
||||||
|
pBack * call->callCount(),
|
||||||
|
c->callCount());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// adjust pNew by sum of geometric series of recursion factor.
|
||||||
|
// Thus we can avoid endless recursion here
|
||||||
|
double fFactor = 1.0 / (1.0 - pForwardNew / c->firstPercentage());
|
||||||
|
double bFactor = 1.0 / (1.0 - pBackNew);
|
||||||
|
#ifdef DEBUG_COVERAGE
|
||||||
|
qDebug("CngCov:%s Recursion - origP %f, actP %f => factor %f, newP %f",
|
||||||
|
spaces+strlen(spaces)-d,
|
||||||
|
c->firstPercentage(), pForwardNew,
|
||||||
|
fFactor, pForwardNew * fFactor);
|
||||||
|
#endif
|
||||||
|
pForwardNew *= fFactor;
|
||||||
|
pBackNew *= bFactor;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit depth
|
||||||
|
if (pForwardNew > 0.0001)
|
||||||
|
c->addCallingCoverage(fList, pForwardNew, pBackNew, d+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_inRecursion)
|
||||||
|
_inRecursion = false;
|
||||||
|
else if (_active)
|
||||||
|
_active = false;
|
||||||
|
}
|
||||||
|
|
102
kcachegrind/libcore/coverage.h
Normal file
102
kcachegrind/libcore/coverage.h
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function Coverage Analysis
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COVERAGE_H
|
||||||
|
#define COVERAGE_H
|
||||||
|
|
||||||
|
#include "tracedata.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coverage of a function.
|
||||||
|
* When analysis is done, every function involved will have a
|
||||||
|
* pointer to an object of this class.
|
||||||
|
*
|
||||||
|
* This function also holds the main routine for coverage analysis,
|
||||||
|
* Coverage::coverage(), as static method.
|
||||||
|
*/
|
||||||
|
class Coverage : public TraceAssociation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/* Direction of coverage analysis */
|
||||||
|
enum CoverageMode { Caller, Called };
|
||||||
|
|
||||||
|
// max depth for distance histogram
|
||||||
|
#define maxHistogramDepthValue 40
|
||||||
|
static const int maxHistogramDepth;
|
||||||
|
|
||||||
|
static const int Rtti;
|
||||||
|
|
||||||
|
Coverage();
|
||||||
|
|
||||||
|
virtual int rtti() { return Rtti; }
|
||||||
|
void init();
|
||||||
|
|
||||||
|
TraceFunction* function() { return _function; }
|
||||||
|
double self() { return _self; }
|
||||||
|
double inclusive() { return _incl; }
|
||||||
|
double firstPercentage() { return _firstPercentage; }
|
||||||
|
double& callCount() { return _callCount; }
|
||||||
|
int minDistance() { return _minDistance; }
|
||||||
|
int maxDistance() { return _maxDistance; }
|
||||||
|
int inclusiveMedian();
|
||||||
|
int selfMedian();
|
||||||
|
double* selfHistogram() { return _selfHisto; }
|
||||||
|
double* inclusiveHistogram() { return _inclHisto; }
|
||||||
|
bool isActive() { return _active; }
|
||||||
|
bool inRecursion() { return _inRecursion; }
|
||||||
|
|
||||||
|
void setSelf(float p) { _self = p; }
|
||||||
|
void setInclusive(float p) { _incl = p; }
|
||||||
|
void setCallCount(float cc) { _callCount = cc; }
|
||||||
|
void setActive(bool a) { _active = a; }
|
||||||
|
void setInRecursion(bool r) { _inRecursion = r; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate coverage of all functions based on function f.
|
||||||
|
* If mode is Called, the coverage of functions called by
|
||||||
|
* f is calculated, otherwise that of functions calling f.
|
||||||
|
* SubCost type ct is used for the analysis.
|
||||||
|
* Self values are undefined for Caller mode.
|
||||||
|
*
|
||||||
|
* Returns list of functions covered.
|
||||||
|
* Coverage degree of returned functions can be get
|
||||||
|
* with function->coverage()->percentage()
|
||||||
|
*/
|
||||||
|
static TraceFunctionList coverage(TraceFunction* f, CoverageMode m,
|
||||||
|
EventType* ct);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addCallerCoverage(TraceFunctionList& l, double, int d);
|
||||||
|
void addCallingCoverage(TraceFunctionList& l, double, double, int d);
|
||||||
|
|
||||||
|
double _self, _incl, _firstPercentage, _callCount;
|
||||||
|
int _minDistance, _maxDistance;
|
||||||
|
bool _active, _inRecursion;
|
||||||
|
double _selfHisto[maxHistogramDepthValue];
|
||||||
|
double _inclHisto[maxHistogramDepthValue];
|
||||||
|
|
||||||
|
// temporary set for one coverage analysis
|
||||||
|
static EventType* _costType;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
620
kcachegrind/libcore/eventtype.cpp
Normal file
620
kcachegrind/libcore/eventtype.cpp
Normal file
|
@ -0,0 +1,620 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002 - 2009 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "eventtype.h"
|
||||||
|
|
||||||
|
#include <QRegExp>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "globalconfig.h"
|
||||||
|
|
||||||
|
//---------------------------------------------------
|
||||||
|
// EventType
|
||||||
|
|
||||||
|
QList<EventType*>* EventType::_knownTypes = 0;
|
||||||
|
|
||||||
|
EventType::EventType(const QString& name, const QString& longName,
|
||||||
|
const QString& formula)
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
_longName = longName;
|
||||||
|
_formula = formula;
|
||||||
|
_isReal = formula.isEmpty();
|
||||||
|
_set = 0;
|
||||||
|
_realIndex = ProfileCostArray::InvalidIndex;
|
||||||
|
_parsed = false;
|
||||||
|
_inParsing = false;
|
||||||
|
|
||||||
|
for (int i=0; i<ProfileCostArray::MaxRealIndex;i++)
|
||||||
|
_coefficient[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventType::setFormula(const QString& formula)
|
||||||
|
{
|
||||||
|
_formula = formula;
|
||||||
|
_realIndex = ProfileCostArray::InvalidIndex;
|
||||||
|
_parsed = false;
|
||||||
|
_isReal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventType::setEventTypeSet(EventTypeSet* m)
|
||||||
|
{
|
||||||
|
_parsed = false;
|
||||||
|
_set = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setting the index to ProfileCostArray::MaxRealIndex makes it a
|
||||||
|
// real type with unspecified index
|
||||||
|
void EventType::setRealIndex(int i)
|
||||||
|
{
|
||||||
|
if (i<0 || i>ProfileCostArray::MaxRealIndex)
|
||||||
|
i=ProfileCostArray::InvalidIndex;
|
||||||
|
|
||||||
|
_realIndex = i;
|
||||||
|
_formula = QString();
|
||||||
|
_isReal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks for existing types and sets coefficients
|
||||||
|
bool EventType::parseFormula()
|
||||||
|
{
|
||||||
|
if (isReal()) return true;
|
||||||
|
if (_parsed) return true;
|
||||||
|
|
||||||
|
if (_inParsing) {
|
||||||
|
qDebug("TraceEventType::parseFormula: Recursion detected.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_set) {
|
||||||
|
qDebug("TraceEventType::parseFormula: Container of this event type unknown!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_inParsing = true;
|
||||||
|
|
||||||
|
for (int i=0; i<ProfileCostArray::MaxRealIndex;i++)
|
||||||
|
_coefficient[i] = 0;
|
||||||
|
_parsedFormula = QString();
|
||||||
|
|
||||||
|
QRegExp rx( "((?:\\+|\\-)?)\\s*(\\d*)\\s*\\*?\\s*(\\w+)" );
|
||||||
|
|
||||||
|
int factor, pos, found, matching;
|
||||||
|
QString costName;
|
||||||
|
EventType* eventType;
|
||||||
|
|
||||||
|
found = 0; // how many types are referenced in formula
|
||||||
|
matching = 0; // how many types actually are defined in profile data
|
||||||
|
pos = 0;
|
||||||
|
while (1) {
|
||||||
|
pos = rx.indexIn(_formula, pos);
|
||||||
|
if (pos<0) break;
|
||||||
|
pos += rx.matchedLength();
|
||||||
|
if (rx.cap(0).isEmpty()) break;
|
||||||
|
found++;
|
||||||
|
|
||||||
|
//qDebug("parseFormula: matched '%s','%s','%s'",
|
||||||
|
// qPrintable(rx.cap(1)), qPrintable(rx.cap(2)), qPrintable(rx.cap(3)));
|
||||||
|
|
||||||
|
costName = rx.cap(3);
|
||||||
|
eventType = _set->type(costName);
|
||||||
|
if (!eventType) {
|
||||||
|
//qDebug("Cost type '%s': In formula cost '%s' unknown.",
|
||||||
|
// qPrintable(_name), qPrintable(costName));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
factor = (rx.cap(2).isEmpty()) ? 1 : rx.cap(2).toInt();
|
||||||
|
if (rx.cap(1) == "-") factor = -factor;
|
||||||
|
if (factor == 0) continue;
|
||||||
|
|
||||||
|
matching++;
|
||||||
|
|
||||||
|
if (!_parsedFormula.isEmpty()) {
|
||||||
|
_parsedFormula += QString(" %1 ").arg((factor>0) ? '+':'-');
|
||||||
|
}
|
||||||
|
else if (factor<0)
|
||||||
|
_parsedFormula += "- ";
|
||||||
|
if ((factor!=-1) && (factor!=1))
|
||||||
|
_parsedFormula += QString::number( (factor>0)?factor:-factor ) + ' ';
|
||||||
|
_parsedFormula += costName;
|
||||||
|
|
||||||
|
if (eventType->isReal())
|
||||||
|
_coefficient[eventType->realIndex()] += factor;
|
||||||
|
else {
|
||||||
|
eventType->parseFormula();
|
||||||
|
for (int i=0; i<ProfileCostArray::MaxRealIndex;i++)
|
||||||
|
_coefficient[i] += factor * eventType->_coefficient[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_inParsing = false;
|
||||||
|
if (found == 0) {
|
||||||
|
// empty formula
|
||||||
|
_parsedFormula = QString("0");
|
||||||
|
_parsed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (matching>0) {
|
||||||
|
_parsed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString EventType::parsedFormula()
|
||||||
|
{
|
||||||
|
if (isReal()) return QString();
|
||||||
|
|
||||||
|
parseFormula();
|
||||||
|
return _parsedFormula;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EventType::parsedRealFormula()
|
||||||
|
{
|
||||||
|
QString res;
|
||||||
|
|
||||||
|
if (!parseFormula()) return res;
|
||||||
|
|
||||||
|
for (int i=0; i<ProfileCostArray::MaxRealIndex;i++) {
|
||||||
|
int c = _coefficient[i];
|
||||||
|
if (c == 0) continue;
|
||||||
|
|
||||||
|
if (!res.isEmpty()) {
|
||||||
|
res += ' ';
|
||||||
|
if (c>0) res += "+ ";
|
||||||
|
}
|
||||||
|
if (c<0) { res += "- "; c = -c; }
|
||||||
|
res += QString::number(c);
|
||||||
|
|
||||||
|
EventType* t = _set->type(i);
|
||||||
|
if (!t) continue;
|
||||||
|
|
||||||
|
if (!t->name().isEmpty())
|
||||||
|
res += QString(" * %1").arg(t->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubCost EventType::subCost(ProfileCostArray* c)
|
||||||
|
{
|
||||||
|
if (_realIndex != ProfileCostArray::InvalidIndex)
|
||||||
|
return c->subCost(_realIndex);
|
||||||
|
|
||||||
|
if (!_parsed) {
|
||||||
|
if (!parseFormula()) return 0;
|
||||||
|
}
|
||||||
|
SubCost res = 0;
|
||||||
|
|
||||||
|
int rc = _set->realCount();
|
||||||
|
for (int i = 0;i<rc;i++)
|
||||||
|
if (_coefficient[i] != 0)
|
||||||
|
res += _coefficient[i] * c->subCost(i);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EventType::histCost(ProfileCostArray* c, double total, double* hist)
|
||||||
|
{
|
||||||
|
if (total == 0.0) return 0;
|
||||||
|
|
||||||
|
if (!_parsed) {
|
||||||
|
if (!parseFormula()) return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = _set->realCount();
|
||||||
|
for (int i = 0;i<rc;i++) {
|
||||||
|
if (_coefficient[i] != 0)
|
||||||
|
hist[i] = _coefficient[i] * c->subCost(i) / total;
|
||||||
|
else
|
||||||
|
hist[i] = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
EventType* EventType::knownRealType(const QString& n)
|
||||||
|
{
|
||||||
|
if (!_knownTypes) return 0;
|
||||||
|
|
||||||
|
foreach (EventType* t, *_knownTypes)
|
||||||
|
if (t->isReal() && (t->name() == n)) {
|
||||||
|
EventType* type = new EventType(*t);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventType* EventType::knownDerivedType(const QString& n)
|
||||||
|
{
|
||||||
|
if (!_knownTypes) return 0;
|
||||||
|
|
||||||
|
foreach (EventType* t, *_knownTypes)
|
||||||
|
if (!t->isReal() && (t->name() == n)) {
|
||||||
|
EventType* type = new EventType(*t);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we take ownership
|
||||||
|
void EventType::add(EventType* t, bool overwriteExisting)
|
||||||
|
{
|
||||||
|
if (!t) return;
|
||||||
|
|
||||||
|
t->setEventTypeSet(0);
|
||||||
|
|
||||||
|
if (!_knownTypes)
|
||||||
|
_knownTypes = new QList<EventType*>;
|
||||||
|
|
||||||
|
/* Already known? */
|
||||||
|
foreach (EventType* kt, *_knownTypes)
|
||||||
|
if (kt->name() == t->name()) {
|
||||||
|
if (overwriteExisting) {
|
||||||
|
// Overwrite old type
|
||||||
|
if (!t->longName().isEmpty() && (t->longName() != t->name()))
|
||||||
|
kt->setLongName(t->longName());
|
||||||
|
if (!t->formula().isEmpty())
|
||||||
|
kt->setFormula(t->formula());
|
||||||
|
}
|
||||||
|
delete t;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->longName().isEmpty()) t->setLongName(t->name());
|
||||||
|
_knownTypes->append(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int EventType::knownTypeCount()
|
||||||
|
{
|
||||||
|
if (!_knownTypes) return 0;
|
||||||
|
|
||||||
|
return _knownTypes->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventType::remove(const QString& n)
|
||||||
|
{
|
||||||
|
if (!_knownTypes) return false;
|
||||||
|
|
||||||
|
foreach (EventType* t, *_knownTypes)
|
||||||
|
if (!t->isReal() && (t->name() == n)) {
|
||||||
|
_knownTypes->removeAll(t);
|
||||||
|
delete t;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventType* EventType::knownType(int i)
|
||||||
|
{
|
||||||
|
if (!_knownTypes) return 0;
|
||||||
|
if (i<0 || i>=(int)_knownTypes->count()) return 0;
|
||||||
|
|
||||||
|
return _knownTypes->at(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------
|
||||||
|
// EventTypeSet
|
||||||
|
|
||||||
|
EventTypeSet::EventTypeSet()
|
||||||
|
{
|
||||||
|
_realCount = 0;
|
||||||
|
_derivedCount = 0;
|
||||||
|
for (int i=0;i<ProfileCostArray::MaxRealIndex;i++) _real[i] = 0;
|
||||||
|
for (int i=0;i<ProfileCostArray::MaxRealIndex;i++) _derived[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventTypeSet::~EventTypeSet()
|
||||||
|
{
|
||||||
|
for (int i=0;i<ProfileCostArray::MaxRealIndex;i++)
|
||||||
|
if (_real[i]) delete _real[i];
|
||||||
|
|
||||||
|
for (int i=0;i<ProfileCostArray::MaxRealIndex;i++)
|
||||||
|
if (_derived[i]) delete _derived[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
EventTypeMapping* EventTypeSet::createMapping(const QString& types)
|
||||||
|
{
|
||||||
|
// first check if there is enough space in the set
|
||||||
|
int newCount = 0;
|
||||||
|
int pos = 0, pos2, len = types.length();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// skip space
|
||||||
|
while((pos<len) && types[pos].isSpace()) pos++;
|
||||||
|
|
||||||
|
pos2 = pos;
|
||||||
|
while((pos2<len) && !types[pos2].isSpace()) pos2++;
|
||||||
|
if (pos2 == pos) break;
|
||||||
|
|
||||||
|
if (realIndex(types.mid(pos,pos2-pos)) == ProfileCostArray::InvalidIndex)
|
||||||
|
newCount++;
|
||||||
|
|
||||||
|
pos = pos2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newCount+_realCount > ProfileCostArray::MaxRealIndex) {
|
||||||
|
qDebug() << "EventTypeSet::createMapping: No space for "
|
||||||
|
<< newCount << " cost entries.";
|
||||||
|
qDebug() << "Increase MaxRealIndexValue in libcore/costitem.h and recompile.";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventTypeMapping* mapping = new EventTypeMapping(this);
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
while (1) {
|
||||||
|
// skip space
|
||||||
|
while((pos<len) && types[pos].isSpace()) pos++;
|
||||||
|
|
||||||
|
pos2 = pos;
|
||||||
|
while((pos2<len) && !types[pos2].isSpace()) pos2++;
|
||||||
|
if (pos2 == pos) break;
|
||||||
|
|
||||||
|
mapping->append(addReal(types.mid(pos,pos2-pos)));
|
||||||
|
|
||||||
|
pos = pos2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EventTypeSet::addReal(const QString& t)
|
||||||
|
{
|
||||||
|
int index = realIndex(t);
|
||||||
|
if (index>=0) return index;
|
||||||
|
|
||||||
|
EventType* ct = EventType::knownRealType(t);
|
||||||
|
if (!ct) ct = new EventType(t, t);
|
||||||
|
|
||||||
|
// make it real
|
||||||
|
ct->setRealIndex();
|
||||||
|
|
||||||
|
return add(ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add an event type to a set
|
||||||
|
// this transfers ownership of the type!
|
||||||
|
int EventTypeSet::add(EventType* et)
|
||||||
|
{
|
||||||
|
if (!et) return ProfileCostArray::InvalidIndex;
|
||||||
|
|
||||||
|
et->setEventTypeSet(this);
|
||||||
|
|
||||||
|
if (et->isReal()) {
|
||||||
|
if (_realCount >= ProfileCostArray::MaxRealIndex) {
|
||||||
|
qDebug("WARNING: Maximum for real event types reached (on adding '%s')",
|
||||||
|
qPrintable(et->name()));
|
||||||
|
return ProfileCostArray::InvalidIndex;
|
||||||
|
}
|
||||||
|
_real[_realCount] = et;
|
||||||
|
et->setRealIndex(_realCount);
|
||||||
|
|
||||||
|
_realCount++;
|
||||||
|
return _realCount-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_derivedCount >= ProfileCostArray::MaxRealIndex) {
|
||||||
|
qDebug("WARNING: Maximum for virtual event types reached (on adding '%s')",
|
||||||
|
qPrintable(et->name()));
|
||||||
|
return ProfileCostArray::InvalidIndex;
|
||||||
|
}
|
||||||
|
_derived[_derivedCount] = et;
|
||||||
|
_derivedCount++;
|
||||||
|
return _derivedCount-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we delete the type: t is invalid when returning true!
|
||||||
|
bool EventTypeSet::remove(EventType* t)
|
||||||
|
{
|
||||||
|
if (!t) return false;
|
||||||
|
if (t->set() != this) return false;
|
||||||
|
|
||||||
|
// do not delete real types
|
||||||
|
if (t->isReal()) return false;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for(i=0;i<_derivedCount;i++)
|
||||||
|
if (_derived[i] == t) break;
|
||||||
|
|
||||||
|
// not found?
|
||||||
|
if (i == _derivedCount) return false;
|
||||||
|
|
||||||
|
// delete known type with same name
|
||||||
|
EventType::remove(t->name());
|
||||||
|
|
||||||
|
// delete this type
|
||||||
|
_derived[i] = 0;
|
||||||
|
delete t;
|
||||||
|
if (i+1 == _derivedCount) {
|
||||||
|
// we can reuse the last index
|
||||||
|
_derivedCount--;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EventType* EventTypeSet::realType(int t)
|
||||||
|
{
|
||||||
|
if (t<0 || t>=_realCount) return 0;
|
||||||
|
return _real[t];
|
||||||
|
}
|
||||||
|
|
||||||
|
EventType* EventTypeSet::derivedType(int t)
|
||||||
|
{
|
||||||
|
if (t<0 || t>=_derivedCount) return 0;
|
||||||
|
return _derived[t];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EventType* EventTypeSet::type(int t)
|
||||||
|
{
|
||||||
|
if (t<0) return 0;
|
||||||
|
if (t<_realCount) return _real[t];
|
||||||
|
|
||||||
|
t -= ProfileCostArray::MaxRealIndex;
|
||||||
|
if (t<0) return 0;
|
||||||
|
if (t<_derivedCount) return _derived[t];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventType* EventTypeSet::type(const QString& name)
|
||||||
|
{
|
||||||
|
for (int i=0;i<_realCount;i++)
|
||||||
|
if (_real[i] && (_real[i]->name() == name))
|
||||||
|
return _real[i];
|
||||||
|
|
||||||
|
for (int i=0;i<_derivedCount;i++)
|
||||||
|
if (_derived[i] && (_derived[i]->name() == name))
|
||||||
|
return _derived[i];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventType* EventTypeSet::typeForLong(const QString& name)
|
||||||
|
{
|
||||||
|
for (int i=0;i<_realCount;i++)
|
||||||
|
if (_real[i] && (_real[i]->longName() == name))
|
||||||
|
return _real[i];
|
||||||
|
|
||||||
|
for (int i=0;i<_derivedCount;i++)
|
||||||
|
if (_derived[i] && (_derived[i]->longName() == name))
|
||||||
|
return _derived[i];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int EventTypeSet::realIndex(const QString& name)
|
||||||
|
{
|
||||||
|
for (int i=0;i<_realCount;i++)
|
||||||
|
if (_real[i] && (_real[i]->name() == name))
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return ProfileCostArray::InvalidIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EventTypeSet::index(const QString& name)
|
||||||
|
{
|
||||||
|
for (int i=0;i<_realCount;i++)
|
||||||
|
if (_real[i] && (_real[i]->name() == name))
|
||||||
|
return i;
|
||||||
|
|
||||||
|
for (int i=0;i<_derivedCount;i++)
|
||||||
|
if (_derived[i] && (_derived[i]->name() == name))
|
||||||
|
return ProfileCostArray::MaxRealIndex + 1 + i;
|
||||||
|
|
||||||
|
return ProfileCostArray::InvalidIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EventTypeSet::addKnownDerivedTypes()
|
||||||
|
{
|
||||||
|
int addCount = 0;
|
||||||
|
int addDiff, i;
|
||||||
|
int knownCount = EventType::knownTypeCount();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
addDiff = 0;
|
||||||
|
for (i=0; i<knownCount; i++) {
|
||||||
|
EventType* t = EventType::knownType(i);
|
||||||
|
if (t->isReal()) continue;
|
||||||
|
if (index(t->name()) != ProfileCostArray::InvalidIndex) continue;
|
||||||
|
t->setEventTypeSet(this);
|
||||||
|
if (t->parseFormula()) {
|
||||||
|
addDiff++;
|
||||||
|
add(new EventType(t->name(), t->longName(), t->formula()));
|
||||||
|
}
|
||||||
|
t->setEventTypeSet(0);
|
||||||
|
}
|
||||||
|
if (addDiff == 0) break;
|
||||||
|
addCount += addDiff;
|
||||||
|
}
|
||||||
|
return addCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------
|
||||||
|
// EventTypeMapping
|
||||||
|
|
||||||
|
EventTypeMapping::EventTypeMapping(EventTypeSet* set)
|
||||||
|
{
|
||||||
|
_set = set;
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventTypeMapping::clear()
|
||||||
|
{
|
||||||
|
_count = 0;
|
||||||
|
_isIdentity = true;
|
||||||
|
_firstUnused = 0;
|
||||||
|
for(int i=0;i<ProfileCostArray::MaxRealIndex;i++) {
|
||||||
|
_realIndex[i] = ProfileCostArray::InvalidIndex;
|
||||||
|
_nextUnused[i] = i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int EventTypeMapping::maxRealIndex(int count)
|
||||||
|
{
|
||||||
|
if (count > _count) count = _count;
|
||||||
|
if (_isIdentity) return count-1;
|
||||||
|
|
||||||
|
int maxIndex = -1;
|
||||||
|
for(int j=0; j<count; j++)
|
||||||
|
if (maxIndex < _realIndex[j])
|
||||||
|
maxIndex = _realIndex[j];
|
||||||
|
return maxIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventTypeMapping::append(const QString& type, bool create)
|
||||||
|
{
|
||||||
|
if (!_set) return false;
|
||||||
|
int index = create ? _set->addReal(type) : _set->realIndex(type);
|
||||||
|
|
||||||
|
return append(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventTypeMapping::append(int type)
|
||||||
|
{
|
||||||
|
if (!_set) return false;
|
||||||
|
if ((type<0) || (type >= _set->realCount())) return false;
|
||||||
|
|
||||||
|
if ( _count >= ProfileCostArray::MaxRealIndex) return false;
|
||||||
|
|
||||||
|
_realIndex[_count] = type;
|
||||||
|
|
||||||
|
if (_isIdentity && (_count != type)) _isIdentity = false;
|
||||||
|
if (type == _firstUnused)
|
||||||
|
_firstUnused = _nextUnused[type];
|
||||||
|
for(int i=0;i<type;i++)
|
||||||
|
if (_nextUnused[i] == type)
|
||||||
|
_nextUnused[i]=_nextUnused[type];
|
||||||
|
|
||||||
|
_count++;
|
||||||
|
return true;
|
||||||
|
}
|
218
kcachegrind/libcore/eventtype.h
Normal file
218
kcachegrind/libcore/eventtype.h
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002 - 2009 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EVENTTYPE_H
|
||||||
|
#define EVENTTYPE_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "subcost.h"
|
||||||
|
#include "costitem.h"
|
||||||
|
|
||||||
|
class EventTypeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cost type, e.g. "L1 Read Miss", short "l1rm".
|
||||||
|
*
|
||||||
|
* We distinguish "real" cost types, where the values come
|
||||||
|
* from the trace file, and "virtual" cost types, which
|
||||||
|
* are calculated from the real ones.
|
||||||
|
*
|
||||||
|
* For a virtual cost type, set a formula to calculate it:
|
||||||
|
* e.g. for "Read Misses" : "l1rm + l2rm".
|
||||||
|
* To allow for parsing, you must specify a EventTypeSet
|
||||||
|
* with according cost types (e.g. "l1rm" and "l2rm" for above formula).
|
||||||
|
*
|
||||||
|
* The cost type with empty name is the "const" cost type.
|
||||||
|
*/
|
||||||
|
class EventType
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <name> is a short (non-localized) identifier for the cost type,
|
||||||
|
* e.g. "l1rm".
|
||||||
|
* <longName> is a long localized string, e.g. "L1 Read Miss"
|
||||||
|
* <formula> uses short names to reference other types
|
||||||
|
*/
|
||||||
|
explicit EventType(const QString& name,
|
||||||
|
const QString& longName = QString(),
|
||||||
|
const QString& formula = QString());
|
||||||
|
|
||||||
|
void setName(const QString& n) { _name = n; }
|
||||||
|
void setLongName(const QString& n) { _longName = n; }
|
||||||
|
void setEventTypeSet(EventTypeSet* m);
|
||||||
|
// enforces event to be derived, even with empty formula
|
||||||
|
void setFormula(const QString&);
|
||||||
|
// default arg is for specifying a real type, but index unknown
|
||||||
|
void setRealIndex(int r = ProfileCostArray::MaxRealIndex);
|
||||||
|
|
||||||
|
const QString& name() { return _name; }
|
||||||
|
const QString& longName() { return _longName; }
|
||||||
|
const QString& formula() { return _formula; }
|
||||||
|
EventTypeSet* set() { return _set; }
|
||||||
|
int realIndex() { return _realIndex; }
|
||||||
|
bool isReal() { return _isReal; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* returns true if all cost type names can be resolved in formula
|
||||||
|
*/
|
||||||
|
bool parseFormula();
|
||||||
|
QString parsedFormula();
|
||||||
|
QString parsedRealFormula();
|
||||||
|
|
||||||
|
SubCost subCost(ProfileCostArray*);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For virtual costs, returns a histogram for use with
|
||||||
|
* partitionPixmap().
|
||||||
|
* Returns maximal real index.
|
||||||
|
*/
|
||||||
|
int histCost(ProfileCostArray* c, double total, double* hist);
|
||||||
|
|
||||||
|
// application wide known types, referenced by short name
|
||||||
|
// next 2 functions return a new type object instance
|
||||||
|
static EventType* knownRealType(const QString&);
|
||||||
|
static EventType* knownDerivedType(const QString&);
|
||||||
|
static void add(EventType*, bool overwriteExisting = true);
|
||||||
|
static bool remove(const QString&);
|
||||||
|
static int knownTypeCount();
|
||||||
|
static EventType* knownType(int);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QString _name, _longName, _formula, _parsedFormula;
|
||||||
|
EventTypeSet* _set;
|
||||||
|
bool _parsed, _inParsing, _isReal;
|
||||||
|
// index MaxRealIndex is for constant addition
|
||||||
|
int _coefficient[MaxRealIndexValue];
|
||||||
|
int _realIndex;
|
||||||
|
|
||||||
|
static QList<EventType*>* _knownTypes;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for managing a set of event types.
|
||||||
|
*
|
||||||
|
* Each event type has an index:
|
||||||
|
* - Real events are in range [0 .. ProfileCostArray:MaxRealIndex[
|
||||||
|
* - Derived events are in range [MaxRealIndex, ...]
|
||||||
|
*/
|
||||||
|
class EventTypeSet
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EventTypeSet();
|
||||||
|
~EventTypeSet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a mapping from indexes into a list of costs to real event types
|
||||||
|
* If <create> is false, checks if this is a existing sub set.
|
||||||
|
*/
|
||||||
|
EventTypeMapping* createMapping(const QString& types);
|
||||||
|
|
||||||
|
// "knows" about some real types
|
||||||
|
int addReal(const QString&);
|
||||||
|
int add(EventType*);
|
||||||
|
bool remove(EventType*);
|
||||||
|
int realCount() { return _realCount; }
|
||||||
|
int derivedCount() { return _derivedCount; }
|
||||||
|
int minDerivedIndex() { return ProfileCostArray::MaxRealIndex; }
|
||||||
|
EventType* type(int);
|
||||||
|
EventType* realType(int);
|
||||||
|
EventType* derivedType(int);
|
||||||
|
EventType* type(const QString&);
|
||||||
|
EventType* typeForLong(const QString&);
|
||||||
|
int realIndex(const QString&);
|
||||||
|
int index(const QString&);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all known derived event types that can be parsed
|
||||||
|
*/
|
||||||
|
int addKnownDerivedTypes();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// we support only a fixed number of real and derived types
|
||||||
|
EventType* _real[MaxRealIndexValue];
|
||||||
|
EventType* _derived[MaxRealIndexValue];
|
||||||
|
int _realCount, _derivedCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A index list into a EventTypeSet
|
||||||
|
*
|
||||||
|
* This ordered list maps from indexes into real cost types
|
||||||
|
* of a set.
|
||||||
|
*
|
||||||
|
* You can define a set of event types by requesting submaps by name.
|
||||||
|
* Every new event type name will get a new real type index.
|
||||||
|
* EventTypeSet s;
|
||||||
|
* m1 = s.createMapping("Event1 Cost1 Cost2"); // returns mapping [0,1,2]
|
||||||
|
* m2 = s.createMapping("Event2 Cost3 Event1"); // returns mapping [3,4,0]
|
||||||
|
* Real types of s will be:
|
||||||
|
* (0:Event1, 1:Cost1, 2:Cost2, 3:Event2, 4:Cost3)
|
||||||
|
*/
|
||||||
|
class EventTypeMapping
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EventTypeMapping(EventTypeSet*);
|
||||||
|
|
||||||
|
bool append(const QString&, bool create=true);
|
||||||
|
bool append(int);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
EventTypeSet* set() { return _set; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get number of used indexes
|
||||||
|
*/
|
||||||
|
int count() { return _count; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this mapping the identity( i.e. realIndex(i)=i ) ?
|
||||||
|
* This often allows for optimizations.
|
||||||
|
*/
|
||||||
|
bool isIdentity() { return _isIdentity; }
|
||||||
|
int realIndex(int i)
|
||||||
|
{ return (i<0 || i>=_count) ? ProfileCostArray::InvalidIndex : _realIndex[i]; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get maximal real index for the first <count> mapping indexes.
|
||||||
|
*/
|
||||||
|
int maxRealIndex(int count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows an iteration over the sorted list of all real indexes not used in
|
||||||
|
* this mapping, up to topIndex (use ProfileCostArray::MaxRealIndex for all).
|
||||||
|
* Usage: for(i = firstUnused(); i < topIndex; i = nextUnused(i)) { LOOP }
|
||||||
|
*/
|
||||||
|
int firstUnused() { return _firstUnused; }
|
||||||
|
int nextUnused(int i) {
|
||||||
|
if (i<0 || i>=ProfileCostArray::MaxRealIndex) return ProfileCostArray::InvalidIndex;
|
||||||
|
return _nextUnused[i]; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
EventTypeSet* _set;
|
||||||
|
int _count, _firstUnused;
|
||||||
|
bool _isIdentity;
|
||||||
|
int _realIndex[MaxRealIndexValue];
|
||||||
|
int _nextUnused[MaxRealIndexValue];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // EVENTTYPE_H
|
187
kcachegrind/libcore/fixcost.cpp
Normal file
187
kcachegrind/libcore/fixcost.cpp
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fixcost.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "addr.h"
|
||||||
|
|
||||||
|
// FixCost
|
||||||
|
|
||||||
|
FixCost::FixCost(TracePart* part, FixPool* pool,
|
||||||
|
TraceFunctionSource* functionSource,
|
||||||
|
PositionSpec& pos,
|
||||||
|
TracePartFunction* partFunction,
|
||||||
|
FixString& s)
|
||||||
|
{
|
||||||
|
int maxCount = part->eventTypeMapping()->count();
|
||||||
|
|
||||||
|
_part = part;
|
||||||
|
_functionSource = functionSource;
|
||||||
|
_pos = pos;
|
||||||
|
|
||||||
|
_cost = (SubCost*) pool->reserve(sizeof(SubCost) * maxCount);
|
||||||
|
s.stripSpaces();
|
||||||
|
int i = 0;
|
||||||
|
while(i<maxCount) {
|
||||||
|
if (!s.stripUInt64(_cost[i])) break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
_count = i;
|
||||||
|
|
||||||
|
if (!pool->allocateReserved(sizeof(SubCost) * _count))
|
||||||
|
_count = 0;
|
||||||
|
|
||||||
|
_nextCostOfPartFunction = partFunction ?
|
||||||
|
partFunction->setFirstFixCost(this) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* FixCost::operator new(size_t size, FixPool* pool)
|
||||||
|
{
|
||||||
|
return pool->allocate(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixCost::addTo(ProfileCostArray* c)
|
||||||
|
{
|
||||||
|
EventTypeMapping* sm = _part->eventTypeMapping();
|
||||||
|
|
||||||
|
int i, realIndex;
|
||||||
|
|
||||||
|
c->reserve(sm->maxRealIndex(_count)+1);
|
||||||
|
for(i=0; i<_count; i++) {
|
||||||
|
realIndex = sm->realIndex(i);
|
||||||
|
c->addCost(realIndex, _cost[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// FixCallCost
|
||||||
|
|
||||||
|
FixCallCost::FixCallCost(TracePart* part, FixPool* pool,
|
||||||
|
TraceFunctionSource* functionSource,
|
||||||
|
unsigned int line, Addr addr,
|
||||||
|
TracePartCall* partCall,
|
||||||
|
SubCost callCount, FixString& s)
|
||||||
|
{
|
||||||
|
if (0) qDebug("Got FixCallCost (addr 0x%s, line %d): calls %s",
|
||||||
|
qPrintable(addr.toString()), line,
|
||||||
|
qPrintable(callCount.pretty()));
|
||||||
|
|
||||||
|
int maxCount = part->eventTypeMapping()->count();
|
||||||
|
|
||||||
|
_part = part;
|
||||||
|
_functionSource = functionSource;
|
||||||
|
_line = line;
|
||||||
|
_addr = addr;
|
||||||
|
|
||||||
|
_cost = (SubCost*) pool->reserve(sizeof(SubCost) * (maxCount+1));
|
||||||
|
s.stripSpaces();
|
||||||
|
int i = 0;
|
||||||
|
while(i<maxCount) {
|
||||||
|
if (!s.stripUInt64(_cost[i])) break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
_count = i;
|
||||||
|
|
||||||
|
if (!pool->allocateReserved(sizeof(SubCost) * (_count+1) ))
|
||||||
|
_count = 0;
|
||||||
|
else
|
||||||
|
_cost[_count] = callCount;
|
||||||
|
|
||||||
|
_nextCostOfPartCall = partCall ? partCall->setFirstFixCallCost(this) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* FixCallCost::operator new(size_t size, FixPool* pool)
|
||||||
|
{
|
||||||
|
return pool->allocate(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixCallCost::addTo(TraceCallCost* c)
|
||||||
|
{
|
||||||
|
EventTypeMapping* sm = _part->eventTypeMapping();
|
||||||
|
|
||||||
|
int i, realIndex;
|
||||||
|
|
||||||
|
for(i=0; i<_count; i++) {
|
||||||
|
realIndex = sm->realIndex(i);
|
||||||
|
c->addCost(realIndex, _cost[i]);
|
||||||
|
}
|
||||||
|
c->addCallCount(_cost[_count]);
|
||||||
|
|
||||||
|
if (0) qDebug("Adding from (addr 0x%s, ln %d): calls %s",
|
||||||
|
qPrintable(_addr.toString()), _line,
|
||||||
|
qPrintable(_cost[_count].pretty()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixCallCost::setMax(ProfileCostArray* c)
|
||||||
|
{
|
||||||
|
EventTypeMapping* sm = _part->eventTypeMapping();
|
||||||
|
|
||||||
|
int i, realIndex;
|
||||||
|
|
||||||
|
for(i=0; i<_count; i++) {
|
||||||
|
realIndex = sm->realIndex(i);
|
||||||
|
c->maxCost(realIndex, _cost[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FixJump
|
||||||
|
|
||||||
|
FixJump::FixJump(TracePart* part, FixPool* pool,
|
||||||
|
unsigned int line, Addr addr,
|
||||||
|
TracePartFunction* partFunction,
|
||||||
|
TraceFunctionSource* source,
|
||||||
|
unsigned int targetLine, Addr targetAddr,
|
||||||
|
TraceFunction* targetFunction,
|
||||||
|
TraceFunctionSource* targetSource,
|
||||||
|
bool isCondJump,
|
||||||
|
SubCost executed, SubCost followed)
|
||||||
|
{
|
||||||
|
_part = part;
|
||||||
|
_source = source;
|
||||||
|
_line = line;
|
||||||
|
_addr = addr;
|
||||||
|
|
||||||
|
_targetFunction = targetFunction;
|
||||||
|
_targetSource = targetSource;
|
||||||
|
_targetLine = targetLine;
|
||||||
|
_targetAddr = targetAddr;
|
||||||
|
|
||||||
|
_isCondJump = isCondJump;
|
||||||
|
|
||||||
|
int size = (isCondJump ? 2 : 1) * sizeof(SubCost);
|
||||||
|
_cost = (SubCost*) pool->allocate(size);
|
||||||
|
_cost[0] = executed;
|
||||||
|
if (isCondJump) _cost[1] = followed;
|
||||||
|
|
||||||
|
_nextJumpOfPartFunction = partFunction ?
|
||||||
|
partFunction->setFirstFixJump(this) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* FixJump::operator new(size_t size, FixPool* pool)
|
||||||
|
{
|
||||||
|
return pool->allocate(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixJump::addTo(TraceJumpCost* jc)
|
||||||
|
{
|
||||||
|
jc->addExecutedCount(_cost[0]);
|
||||||
|
if (_isCondJump)
|
||||||
|
jc->addFollowedCount(_cost[1]);
|
||||||
|
}
|
183
kcachegrind/libcore/fixcost.h
Normal file
183
kcachegrind/libcore/fixcost.h
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FIXCOST_H
|
||||||
|
#define FIXCOST_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting USE_FIXCOST to 1 enables a memory space hack:
|
||||||
|
* For some data, build up internal data model lazy by using
|
||||||
|
* the Fix*Cost classes, which are simple copies from input data.
|
||||||
|
*/
|
||||||
|
#define USE_FIXCOST 1
|
||||||
|
|
||||||
|
#include "tracedata.h"
|
||||||
|
#include "pool.h"
|
||||||
|
|
||||||
|
class PositionSpec
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PositionSpec()
|
||||||
|
{ fromLine = 0, toLine = 0, fromAddr = 0, toAddr = 0; }
|
||||||
|
PositionSpec(uint l1, uint l2, Addr a1, Addr a2)
|
||||||
|
{ fromLine = l1, toLine = l2, fromAddr = a1, toAddr = a2; }
|
||||||
|
|
||||||
|
bool isLineRegion() const { return (fromLine != toLine); }
|
||||||
|
bool isAddrRegion() const { return (fromAddr != toAddr); }
|
||||||
|
|
||||||
|
uint fromLine, toLine;
|
||||||
|
Addr fromAddr, toAddr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class holding an unchangable cost item of an input file.
|
||||||
|
*
|
||||||
|
* As there can be a lot of such cost items, we use our own
|
||||||
|
* allocator which uses FixPool
|
||||||
|
*/
|
||||||
|
class FixCost
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
FixCost(TracePart*, FixPool*,
|
||||||
|
TraceFunctionSource*,
|
||||||
|
PositionSpec&,
|
||||||
|
TracePartFunction*,
|
||||||
|
FixString&);
|
||||||
|
|
||||||
|
void *operator new(size_t size, FixPool*);
|
||||||
|
|
||||||
|
void addTo(ProfileCostArray*);
|
||||||
|
|
||||||
|
TracePart* part() const { return _part; }
|
||||||
|
bool isLineRegion() const { return _pos.isLineRegion(); }
|
||||||
|
bool isAddrRegion() const { return _pos.isAddrRegion(); }
|
||||||
|
uint fromLine() const { return _pos.fromLine; }
|
||||||
|
uint line() const { return _pos.fromLine; }
|
||||||
|
uint toLine() const { return _pos.toLine; }
|
||||||
|
Addr fromAddr() const { return _pos.fromAddr; }
|
||||||
|
Addr addr() const { return _pos.fromAddr; }
|
||||||
|
Addr toAddr() const { return _pos.toAddr; }
|
||||||
|
TraceFunctionSource* functionSource() const { return _functionSource; }
|
||||||
|
|
||||||
|
FixCost* nextCostOfPartFunction() const
|
||||||
|
{ return _nextCostOfPartFunction; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _count;
|
||||||
|
SubCost* _cost;
|
||||||
|
PositionSpec _pos;
|
||||||
|
|
||||||
|
TracePart* _part;
|
||||||
|
TraceFunctionSource* _functionSource;
|
||||||
|
FixCost *_nextCostOfPartFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A FixCallCost will be inserted into a
|
||||||
|
* - TracePartCall to keep source/target function info
|
||||||
|
* - TraceFunctionSourceFile to keep file info of call source
|
||||||
|
*/
|
||||||
|
class FixCallCost
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
FixCallCost(TracePart*, FixPool*,
|
||||||
|
TraceFunctionSource*,
|
||||||
|
unsigned int line,
|
||||||
|
Addr addr,
|
||||||
|
TracePartCall*,
|
||||||
|
SubCost, FixString&);
|
||||||
|
|
||||||
|
void *operator new(size_t size, FixPool*);
|
||||||
|
|
||||||
|
void addTo(TraceCallCost*);
|
||||||
|
void setMax(ProfileCostArray*);
|
||||||
|
|
||||||
|
TracePart* part() const { return _part; }
|
||||||
|
unsigned int line() const { return _line; }
|
||||||
|
Addr addr() const { return _addr; }
|
||||||
|
SubCost callCount() const { return _cost[_count]; }
|
||||||
|
TraceFunctionSource* functionSource() const { return _functionSource; }
|
||||||
|
FixCallCost* nextCostOfPartCall() const
|
||||||
|
{ return _nextCostOfPartCall; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// we use 1 SubCost more than _count: _cost[_count] is the call count
|
||||||
|
int _count;
|
||||||
|
SubCost* _cost;
|
||||||
|
unsigned int _line;
|
||||||
|
Addr _addr;
|
||||||
|
|
||||||
|
TracePart* _part;
|
||||||
|
TraceFunctionSource* _functionSource;
|
||||||
|
FixCallCost* _nextCostOfPartCall;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class holding a jump (mostly) inside of a function
|
||||||
|
*/
|
||||||
|
class FixJump
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
FixJump(TracePart*, FixPool*,
|
||||||
|
/* source position */
|
||||||
|
unsigned int line, Addr addr,
|
||||||
|
TracePartFunction*, TraceFunctionSource*,
|
||||||
|
/* target position */
|
||||||
|
unsigned int targetLine, Addr targetAddr,
|
||||||
|
TraceFunction*, TraceFunctionSource*,
|
||||||
|
bool isCondJump,
|
||||||
|
SubCost, SubCost);
|
||||||
|
|
||||||
|
void *operator new(size_t size, FixPool*);
|
||||||
|
|
||||||
|
void addTo(TraceJumpCost*);
|
||||||
|
|
||||||
|
TracePart* part() const { return _part; }
|
||||||
|
unsigned int line() const { return _line; }
|
||||||
|
Addr addr() const { return _addr; }
|
||||||
|
TraceFunctionSource* source() const { return _source; }
|
||||||
|
TraceFunction* targetFunction() const { return _targetFunction; }
|
||||||
|
unsigned int targetLine() const { return _targetLine; }
|
||||||
|
Addr targetAddr() const { return _targetAddr; }
|
||||||
|
TraceFunctionSource* targetSource() const { return _targetSource; }
|
||||||
|
bool isCondJump() const { return _isCondJump; }
|
||||||
|
SubCost executedCount() const { return _cost[0]; }
|
||||||
|
SubCost followedCount() const
|
||||||
|
{ return _isCondJump ? _cost[1] : SubCost(0); }
|
||||||
|
|
||||||
|
FixJump* nextJumpOfPartFunction() const
|
||||||
|
{ return _nextJumpOfPartFunction; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _isCondJump;
|
||||||
|
SubCost* _cost;
|
||||||
|
unsigned int _line, _targetLine;
|
||||||
|
Addr _addr, _targetAddr;
|
||||||
|
|
||||||
|
TracePart* _part;
|
||||||
|
TraceFunctionSource *_source, *_targetSource;
|
||||||
|
TraceFunction* _targetFunction;
|
||||||
|
FixJump *_nextJumpOfPartFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
476
kcachegrind/libcore/globalconfig.cpp
Normal file
476
kcachegrind/libcore/globalconfig.cpp
Normal file
|
@ -0,0 +1,476 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002-2008 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global configuration for KCachegrind
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "globalconfig.h"
|
||||||
|
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "tracedata.h"
|
||||||
|
|
||||||
|
|
||||||
|
// GlobalConfig defaults
|
||||||
|
#define DEFAULT_SHOWPERCENTAGE true
|
||||||
|
#define DEFAULT_SHOWEXPANDED false
|
||||||
|
#define DEFAULT_SHOWCYCLES true
|
||||||
|
#define DEFAULT_HIDETEMPLATES false
|
||||||
|
#define DEFAULT_CYCLECUT 0.0
|
||||||
|
#define DEFAULT_PERCENTPRECISION 2
|
||||||
|
#define DEFAULT_MAXSYMBOLLENGTH 30
|
||||||
|
#define DEFAULT_MAXSYMBOLCOUNT 10
|
||||||
|
#define DEFAULT_MAXLISTCOUNT 100
|
||||||
|
#define DEFAULT_CONTEXT 3
|
||||||
|
#define DEFAULT_NOCOSTINSIDE 20
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// GlobalConfig
|
||||||
|
//
|
||||||
|
|
||||||
|
// Some predefined event types...
|
||||||
|
QStringList GlobalConfig::knownTypes()
|
||||||
|
{
|
||||||
|
QStringList l;
|
||||||
|
|
||||||
|
l << "Ir" << "Dr" << "Dw"
|
||||||
|
<< "I1mr" << "D1mr" << "D1mw" << "L1m";
|
||||||
|
// Valgrind < 3.6.0
|
||||||
|
l << "I2mr" << "D2mr" << "D2mw" << "L2m";
|
||||||
|
// Valgrind 3.6.0: L2 events changed to to LL (last level) events
|
||||||
|
l << "ILmr" << "DLmr" << "DLmw" << "LLm";
|
||||||
|
|
||||||
|
// branch simulation
|
||||||
|
l << "Bi" << "Bim" << "Bc" << "Bcm" << "Bm";
|
||||||
|
|
||||||
|
// global bus events (e.g. CAS)
|
||||||
|
l << "Ge";
|
||||||
|
|
||||||
|
l << "Smp" << "Sys" << "User" << "CEst";
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString GlobalConfig::knownFormula(const QString& name)
|
||||||
|
{
|
||||||
|
if (name == "L1m") return QString("I1mr + D1mr + D1mw");
|
||||||
|
if (name == "L2m") return QString("I2mr + D2mr + D2mw");
|
||||||
|
if (name == "LLm") return QString("ILmr + DLmr + DLmw");
|
||||||
|
if (name == "Bm") return QString("Bim + Bcm");
|
||||||
|
if (name == "CEst")
|
||||||
|
return QString("Ir + 10 Bm + 10 L1m + 20 Ge + 100 L2m + 100 LLm");
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GlobalConfig::knownLongName(const QString& name)
|
||||||
|
{
|
||||||
|
if (name == "Ir") return QObject::tr("Instruction Fetch");
|
||||||
|
if (name == "Dr") return QObject::tr("Data Read Access");
|
||||||
|
if (name == "Dw") return QObject::tr("Data Write Access");
|
||||||
|
if (name == "I1mr") return QObject::tr("L1 Instr. Fetch Miss");
|
||||||
|
if (name == "D1mr") return QObject::tr("L1 Data Read Miss");
|
||||||
|
if (name == "D1mw") return QObject::tr("L1 Data Write Miss");
|
||||||
|
if (name == "I2mr") return QObject::tr("L2 Instr. Fetch Miss");
|
||||||
|
if (name == "D2mr") return QObject::tr("L2 Data Read Miss");
|
||||||
|
if (name == "D2mw") return QObject::tr("L2 Data Write Miss");
|
||||||
|
if (name == "ILmr") return QObject::tr("LL Instr. Fetch Miss");
|
||||||
|
if (name == "DLmr") return QObject::tr("LL Data Read Miss");
|
||||||
|
if (name == "DLmw") return QObject::tr("LL Data Write Miss");
|
||||||
|
if (name == "L1m") return QObject::tr("L1 Miss Sum");
|
||||||
|
if (name == "L2m") return QObject::tr("L2 Miss Sum");
|
||||||
|
if (name == "LLm") return QObject::tr("Last-level Miss Sum");
|
||||||
|
if (name == "Bi") return QObject::tr("Indirect Branch");
|
||||||
|
if (name == "Bim") return QObject::tr("Mispredicted Ind. Branch");
|
||||||
|
if (name == "Bc") return QObject::tr("Conditional Branch");
|
||||||
|
if (name == "Bcm") return QObject::tr("Mispredicted Cond. Branch");
|
||||||
|
if (name == "Bm") return QObject::tr("Mispredicted Branch");
|
||||||
|
if (name == "Ge") return QObject::tr("Global Bus Event");
|
||||||
|
if (name == "Smp") return QObject::tr("Samples");
|
||||||
|
if (name == "Sys") return QObject::tr("System Time");
|
||||||
|
if (name == "User") return QObject::tr("User Time");
|
||||||
|
if (name == "CEst") return QObject::tr("Cycle Estimation");
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GlobalConfig* GlobalConfig::_config = 0;
|
||||||
|
|
||||||
|
GlobalConfig::GlobalConfig()
|
||||||
|
{
|
||||||
|
_config = 0;
|
||||||
|
|
||||||
|
// general presentation options
|
||||||
|
_showPercentage = DEFAULT_SHOWPERCENTAGE;
|
||||||
|
_showExpanded = DEFAULT_SHOWEXPANDED;
|
||||||
|
_showCycles = DEFAULT_SHOWCYCLES;
|
||||||
|
_cycleCut = DEFAULT_CYCLECUT;
|
||||||
|
_percentPrecision = DEFAULT_PERCENTPRECISION;
|
||||||
|
_hideTemplates = DEFAULT_HIDETEMPLATES;
|
||||||
|
|
||||||
|
// max symbol count/length in tooltip/popup
|
||||||
|
_maxSymbolLength = DEFAULT_MAXSYMBOLLENGTH;
|
||||||
|
_maxSymbolCount = DEFAULT_MAXSYMBOLCOUNT;
|
||||||
|
_maxListCount = DEFAULT_MAXLISTCOUNT;
|
||||||
|
|
||||||
|
// annotation behaviour
|
||||||
|
_context = DEFAULT_CONTEXT;
|
||||||
|
_noCostInside = DEFAULT_NOCOSTINSIDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalConfig::~GlobalConfig()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalConfig* GlobalConfig::config()
|
||||||
|
{
|
||||||
|
if (_config == 0)
|
||||||
|
_config = new GlobalConfig();
|
||||||
|
|
||||||
|
return _config;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GlobalConfig::saveOptions()
|
||||||
|
{
|
||||||
|
// source options
|
||||||
|
ConfigGroup* sourceConfig = ConfigStorage::group("Source");
|
||||||
|
sourceConfig->setValue("Dirs", _generalSourceDirs);
|
||||||
|
QHashIterator<QString,QStringList> it( _objectSourceDirs );
|
||||||
|
int count = 1;
|
||||||
|
while( it.hasNext() ) {
|
||||||
|
it.next();
|
||||||
|
sourceConfig->setValue( QString("Object%1").arg(count),
|
||||||
|
it.key() );
|
||||||
|
sourceConfig->setValue( QString("Dirs%1").arg(count),
|
||||||
|
it.value() );
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
sourceConfig->setValue("Count", count-1);
|
||||||
|
delete sourceConfig;
|
||||||
|
|
||||||
|
// general options
|
||||||
|
ConfigGroup* generalConfig = ConfigStorage::group("GeneralSettings");
|
||||||
|
generalConfig->setValue("ShowPercentage", _showPercentage,
|
||||||
|
DEFAULT_SHOWPERCENTAGE);
|
||||||
|
generalConfig->setValue("ShowExpanded", _showExpanded,
|
||||||
|
DEFAULT_SHOWEXPANDED);
|
||||||
|
generalConfig->setValue("ShowCycles", _showCycles,
|
||||||
|
DEFAULT_SHOWCYCLES);
|
||||||
|
generalConfig->setValue("CycleCut", _cycleCut,
|
||||||
|
DEFAULT_CYCLECUT);
|
||||||
|
generalConfig->setValue("PercentPrecision", _percentPrecision,
|
||||||
|
DEFAULT_PERCENTPRECISION);
|
||||||
|
generalConfig->setValue("MaxSymbolLength", _maxSymbolLength,
|
||||||
|
DEFAULT_MAXSYMBOLLENGTH);
|
||||||
|
generalConfig->setValue("MaxSymbolCount", _maxSymbolCount,
|
||||||
|
DEFAULT_MAXSYMBOLCOUNT);
|
||||||
|
generalConfig->setValue("MaxListCount", _maxListCount,
|
||||||
|
DEFAULT_MAXLISTCOUNT);
|
||||||
|
generalConfig->setValue("Context", _context,
|
||||||
|
DEFAULT_CONTEXT);
|
||||||
|
generalConfig->setValue("NoCostInside", _noCostInside,
|
||||||
|
DEFAULT_NOCOSTINSIDE);
|
||||||
|
generalConfig->setValue("HideTemplates", _hideTemplates,
|
||||||
|
DEFAULT_HIDETEMPLATES);
|
||||||
|
delete generalConfig;
|
||||||
|
|
||||||
|
// event types
|
||||||
|
ConfigGroup* etConfig = ConfigStorage::group("EventTypes");
|
||||||
|
int etCount = EventType::knownTypeCount();
|
||||||
|
etConfig->setValue( "Count", etCount);
|
||||||
|
for (int i=0; i<etCount; i++) {
|
||||||
|
EventType* t = EventType::knownType(i);
|
||||||
|
etConfig->setValue( QString("Name%1").arg(i+1), t->name());
|
||||||
|
etConfig->setValue( QString("Longname%1").arg(i+1),
|
||||||
|
t->longName(),
|
||||||
|
knownLongName(t->name()) );
|
||||||
|
etConfig->setValue( QString("Formula%1").arg(i+1),
|
||||||
|
t->formula(), knownFormula(t->name()) );
|
||||||
|
}
|
||||||
|
delete etConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::readOptions()
|
||||||
|
{
|
||||||
|
int i, count;
|
||||||
|
|
||||||
|
// source options
|
||||||
|
ConfigGroup* sourceConfig = ConfigStorage::group("Source");
|
||||||
|
QStringList dirs;
|
||||||
|
dirs = sourceConfig->value("Dirs", QStringList()).toStringList();
|
||||||
|
if (dirs.count()>0) _generalSourceDirs = dirs;
|
||||||
|
count = sourceConfig->value("Count", 0).toInt();
|
||||||
|
_objectSourceDirs.clear();
|
||||||
|
for (i=1;i<=count;i++) {
|
||||||
|
QString n = sourceConfig->value(QString("Object%1").arg(i),
|
||||||
|
QString()).toString();
|
||||||
|
dirs = sourceConfig->value(QString("Dirs%1").arg(i),
|
||||||
|
QStringList()).toStringList();
|
||||||
|
|
||||||
|
if (n.isEmpty() || (dirs.count()==0)) continue;
|
||||||
|
|
||||||
|
_objectSourceDirs.insert(n, dirs);
|
||||||
|
}
|
||||||
|
delete sourceConfig;
|
||||||
|
|
||||||
|
// general options
|
||||||
|
ConfigGroup* generalConfig = ConfigStorage::group("GeneralSettings");
|
||||||
|
_showPercentage = generalConfig->value("ShowPercentage",
|
||||||
|
DEFAULT_SHOWPERCENTAGE).toBool();
|
||||||
|
_showExpanded = generalConfig->value("ShowExpanded",
|
||||||
|
DEFAULT_SHOWEXPANDED).toBool();
|
||||||
|
_showCycles = generalConfig->value("ShowCycles",
|
||||||
|
DEFAULT_SHOWCYCLES).toBool();
|
||||||
|
_cycleCut = generalConfig->value("CycleCut",
|
||||||
|
DEFAULT_CYCLECUT).toDouble();
|
||||||
|
_percentPrecision = generalConfig->value("PercentPrecision",
|
||||||
|
DEFAULT_PERCENTPRECISION).toInt();
|
||||||
|
_maxSymbolLength = generalConfig->value("MaxSymbolLength",
|
||||||
|
DEFAULT_MAXSYMBOLLENGTH).toInt();
|
||||||
|
_maxSymbolCount = generalConfig->value("MaxSymbolCount",
|
||||||
|
DEFAULT_MAXSYMBOLCOUNT).toInt();
|
||||||
|
_maxListCount = generalConfig->value("MaxListCount",
|
||||||
|
DEFAULT_MAXLISTCOUNT).toInt();
|
||||||
|
_context = generalConfig->value("Context",
|
||||||
|
DEFAULT_CONTEXT).toInt();
|
||||||
|
_noCostInside = generalConfig->value("NoCostInside",
|
||||||
|
DEFAULT_NOCOSTINSIDE).toInt();
|
||||||
|
_hideTemplates = generalConfig->value("HideTemplates",
|
||||||
|
DEFAULT_HIDETEMPLATES).toBool();
|
||||||
|
delete generalConfig;
|
||||||
|
|
||||||
|
// event types
|
||||||
|
if (EventType::knownTypeCount() >0) return; // already read
|
||||||
|
ConfigGroup* etConfig = ConfigStorage::group("EventTypes");
|
||||||
|
int etCount = etConfig->value("Count", 0).toInt();
|
||||||
|
|
||||||
|
for (int i=1;i<=etCount;i++) {
|
||||||
|
QString n = etConfig->value(QString("Name%1").arg(i),
|
||||||
|
QString()).toString();
|
||||||
|
QString l = etConfig->value(QString("Longname%1").arg(i),
|
||||||
|
QString()).toString();
|
||||||
|
if (l.isEmpty()) l = knownLongName(n);
|
||||||
|
QString f = etConfig->value(QString("Formula%1").arg(i),
|
||||||
|
QString()).toString();
|
||||||
|
if (f.isEmpty()) f = knownFormula(n);
|
||||||
|
|
||||||
|
EventType::add(new EventType(n, l, f));
|
||||||
|
}
|
||||||
|
|
||||||
|
// this does only add yet non-existing types
|
||||||
|
addDefaultTypes();
|
||||||
|
|
||||||
|
delete etConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::addDefaultTypes()
|
||||||
|
{
|
||||||
|
QString longName, formula;
|
||||||
|
EventType* ct;
|
||||||
|
QStringList l = knownTypes();
|
||||||
|
for ( QStringList::const_iterator it = l.constBegin();
|
||||||
|
it != l.constEnd(); ++it ) {
|
||||||
|
longName = knownLongName(*it);
|
||||||
|
formula = knownFormula(*it);
|
||||||
|
ct = new EventType(*it, longName, formula);
|
||||||
|
EventType::add(ct, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gives back a list of all Source Base Directories of Objects in
|
||||||
|
* current trace. If a special object is given in 2nd argument,
|
||||||
|
* put its Source Base in front.
|
||||||
|
*/
|
||||||
|
QStringList GlobalConfig::sourceDirs(TraceData* data, TraceObject* o)
|
||||||
|
{
|
||||||
|
QStringList l = config()->_generalSourceDirs, ol, ol2;
|
||||||
|
TraceObjectMap::Iterator oit;
|
||||||
|
for ( oit = data->objectMap().begin();
|
||||||
|
oit != data->objectMap().end(); ++oit ) {
|
||||||
|
ol = config()->_objectSourceDirs[(*oit).name()];
|
||||||
|
if (&(*oit) == o) {
|
||||||
|
ol2 = ol;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0;i<ol.count();i++)
|
||||||
|
l.prepend( ol[i] );
|
||||||
|
}
|
||||||
|
for(int i=0;i<ol2.count();i++)
|
||||||
|
l.prepend( ol2[i] );
|
||||||
|
|
||||||
|
if (0) qDebug() << "GlobalConfig::sourceDirs: " << l.join(":");
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GlobalConfig::showPercentage()
|
||||||
|
{
|
||||||
|
return config()->_showPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GlobalConfig::showExpanded()
|
||||||
|
{
|
||||||
|
return config()->_showExpanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GlobalConfig::showCycles()
|
||||||
|
{
|
||||||
|
return config()->_showCycles;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GlobalConfig::hideTemplates()
|
||||||
|
{
|
||||||
|
return config()->_hideTemplates;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::setShowPercentage(bool s)
|
||||||
|
{
|
||||||
|
GlobalConfig* c = config();
|
||||||
|
if (c->_showPercentage == s) return;
|
||||||
|
|
||||||
|
c->_showPercentage = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::setShowExpanded(bool s)
|
||||||
|
{
|
||||||
|
GlobalConfig* c = config();
|
||||||
|
if (c->_showExpanded == s) return;
|
||||||
|
|
||||||
|
c->_showExpanded = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::setShowCycles(bool s)
|
||||||
|
{
|
||||||
|
GlobalConfig* c = config();
|
||||||
|
if (c->_showCycles == s) return;
|
||||||
|
|
||||||
|
c->_showCycles = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::setHideTemplates(bool s)
|
||||||
|
{
|
||||||
|
GlobalConfig* c = config();
|
||||||
|
if (c->_hideTemplates == s) return;
|
||||||
|
|
||||||
|
c->_hideTemplates = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
double GlobalConfig::cycleCut()
|
||||||
|
{
|
||||||
|
return config()->_cycleCut;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GlobalConfig::percentPrecision()
|
||||||
|
{
|
||||||
|
return config()->_percentPrecision;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GlobalConfig::maxSymbolLength()
|
||||||
|
{
|
||||||
|
return config()->_maxSymbolLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GlobalConfig::shortenSymbol(const QString& s)
|
||||||
|
{
|
||||||
|
if(s.length() > config()->_maxSymbolLength)
|
||||||
|
return s.left(config()->_maxSymbolLength) + QLatin1String("...");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GlobalConfig::maxListCount()
|
||||||
|
{
|
||||||
|
return config()->_maxListCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GlobalConfig::maxSymbolCount()
|
||||||
|
{
|
||||||
|
return config()->_maxSymbolCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GlobalConfig::context()
|
||||||
|
{
|
||||||
|
return config()->_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GlobalConfig::noCostInside()
|
||||||
|
{
|
||||||
|
return config()->_noCostInside;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::setPercentPrecision(int v)
|
||||||
|
{
|
||||||
|
if ((v<1) || (v >5)) return;
|
||||||
|
_percentPrecision = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::setMaxSymbolLength(int v)
|
||||||
|
{
|
||||||
|
if ((v<1) || (v >1000)) return;
|
||||||
|
_maxSymbolLength = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::setMaxSymbolCount(int v)
|
||||||
|
{
|
||||||
|
if ((v<1) || (v >50)) return;
|
||||||
|
_maxSymbolCount = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::setMaxListCount(int v)
|
||||||
|
{
|
||||||
|
if ((v<1) || (v >500)) return;
|
||||||
|
_maxListCount = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::setContext(int v)
|
||||||
|
{
|
||||||
|
if ((v<1) || (v >500)) return;
|
||||||
|
_context = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList& GlobalConfig::generalSourceDirs()
|
||||||
|
{
|
||||||
|
return _generalSourceDirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList GlobalConfig::objectSourceDirs(QString obj)
|
||||||
|
{
|
||||||
|
if (_objectSourceDirs.contains(obj))
|
||||||
|
return _objectSourceDirs[obj];
|
||||||
|
else
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::setGeneralSourceDirs(QStringList dirs)
|
||||||
|
{
|
||||||
|
_generalSourceDirs = dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalConfig::setObjectSourceDirs(QString obj, QStringList dirs)
|
||||||
|
{
|
||||||
|
if (dirs.count() == 0)
|
||||||
|
_objectSourceDirs.remove(obj);
|
||||||
|
else
|
||||||
|
_objectSourceDirs.insert(obj, dirs);
|
||||||
|
}
|
111
kcachegrind/libcore/globalconfig.h
Normal file
111
kcachegrind/libcore/globalconfig.h
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002-2008 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global configuration for KCachegrind (only non-GUI options)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GLOBALCONFIG_H
|
||||||
|
#define GLOBALCONFIG_H
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
#include "tracedata.h"
|
||||||
|
|
||||||
|
class GlobalConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global configuration (only non-GUI options).
|
||||||
|
* A singleton.
|
||||||
|
*/
|
||||||
|
class GlobalConfig
|
||||||
|
{
|
||||||
|
friend class ConfigDlg;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GlobalConfig();
|
||||||
|
virtual ~GlobalConfig();
|
||||||
|
|
||||||
|
// gets the singleton instance
|
||||||
|
static GlobalConfig* config();
|
||||||
|
|
||||||
|
virtual void saveOptions();
|
||||||
|
virtual void readOptions();
|
||||||
|
|
||||||
|
static QStringList sourceDirs(TraceData*, TraceObject* o = 0);
|
||||||
|
static bool showPercentage();
|
||||||
|
static bool showExpanded();
|
||||||
|
static bool showCycles();
|
||||||
|
static bool hideTemplates();
|
||||||
|
|
||||||
|
// lower percentage limit of cost items filled into lists
|
||||||
|
static int percentPrecision();
|
||||||
|
// max symbol lengths/count in tooltip/popup
|
||||||
|
static int maxSymbolLength();
|
||||||
|
// strip a symbol name according to <maxSymbolLength>
|
||||||
|
static QString shortenSymbol(const QString&);
|
||||||
|
static int maxSymbolCount();
|
||||||
|
// max. number of items in lists
|
||||||
|
static int maxListCount();
|
||||||
|
|
||||||
|
// how many lines of context to show before/after annotated source/assembler
|
||||||
|
static int context();
|
||||||
|
// how many lines without cost are still regarded as inside a function
|
||||||
|
static int noCostInside();
|
||||||
|
|
||||||
|
const QStringList& generalSourceDirs();
|
||||||
|
QStringList objectSourceDirs(QString);
|
||||||
|
void setGeneralSourceDirs(QStringList);
|
||||||
|
void setObjectSourceDirs(QString, QStringList);
|
||||||
|
|
||||||
|
void setPercentPrecision(int);
|
||||||
|
void setMaxSymbolLength(int);
|
||||||
|
void setMaxSymbolCount(int);
|
||||||
|
void setMaxListCount(int);
|
||||||
|
void setContext(int);
|
||||||
|
|
||||||
|
static void setShowPercentage(bool);
|
||||||
|
static void setShowExpanded(bool);
|
||||||
|
|
||||||
|
static void setShowCycles(bool);
|
||||||
|
|
||||||
|
static void setHideTemplates(bool);
|
||||||
|
// upper limit for cutting of a call in cycle detection
|
||||||
|
static double cycleCut();
|
||||||
|
|
||||||
|
void addDefaultTypes();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QStringList knownTypes();
|
||||||
|
QString knownFormula(const QString& name);
|
||||||
|
QString knownLongName(const QString& name);
|
||||||
|
|
||||||
|
QStringList _generalSourceDirs;
|
||||||
|
QHash<QString, QStringList> _objectSourceDirs;
|
||||||
|
|
||||||
|
bool _showPercentage, _showExpanded, _showCycles, _hideTemplates;
|
||||||
|
double _cycleCut;
|
||||||
|
int _percentPrecision;
|
||||||
|
int _maxSymbolLength, _maxSymbolCount, _maxListCount;
|
||||||
|
int _context, _noCostInside;
|
||||||
|
|
||||||
|
static GlobalConfig* _config;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GLOBALCONFIG_H
|
37
kcachegrind/libcore/libcore.pri
Normal file
37
kcachegrind/libcore/libcore.pri
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
DEPENDPATH += $$PWD
|
||||||
|
|
||||||
|
NHEADERS += \
|
||||||
|
$$PWD/context.h \
|
||||||
|
$$PWD/costitem.h \
|
||||||
|
$$PWD/subcost.h \
|
||||||
|
$$PWD/eventtype.h \
|
||||||
|
$$PWD/addr.h \
|
||||||
|
$$PWD/config.h \
|
||||||
|
$$PWD/globalconfig.h \
|
||||||
|
$$PWD/tracedata.h \
|
||||||
|
$$PWD/utils.h \
|
||||||
|
$$PWD/logger.h \
|
||||||
|
$$PWD/loader.h \
|
||||||
|
$$PWD/fixcost.h \
|
||||||
|
$$PWD/pool.h \
|
||||||
|
$$PWD/coverage.h \
|
||||||
|
$$PWD/stackbrowser.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/context.cpp \
|
||||||
|
$$PWD/costitem.cpp \
|
||||||
|
$$PWD/subcost.cpp \
|
||||||
|
$$PWD/eventtype.cpp \
|
||||||
|
$$PWD/addr.cpp \
|
||||||
|
$$PWD/cachegrindloader.cpp \
|
||||||
|
$$PWD/config.cpp \
|
||||||
|
$$PWD/coverage.cpp \
|
||||||
|
$$PWD/fixcost.cpp \
|
||||||
|
$$PWD/globalconfig.cpp \
|
||||||
|
$$PWD/loader.cpp \
|
||||||
|
$$PWD/logger.cpp \
|
||||||
|
$$PWD/pool.cpp \
|
||||||
|
$$PWD/stackbrowser.cpp \
|
||||||
|
$$PWD/tracedata.cpp \
|
||||||
|
$$PWD/utils.cpp
|
121
kcachegrind/libcore/loader.cpp
Normal file
121
kcachegrind/libcore/loader.cpp
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002 - 2010 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Base class for loaders of profiling data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "loader.h"
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
/// Loader
|
||||||
|
|
||||||
|
QList<Loader*> Loader::_loaderList;
|
||||||
|
|
||||||
|
Loader::Loader(const QString& name, const QString& desc)
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
_description = desc;
|
||||||
|
|
||||||
|
_logger = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader::~Loader()
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool Loader::canLoad(QIODevice*)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Loader::load(TraceData*, QIODevice*, const QString&)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader* Loader::matchingLoader(QIODevice* file)
|
||||||
|
{
|
||||||
|
foreach (Loader* l, _loaderList)
|
||||||
|
if (l->canLoad(file))
|
||||||
|
return l;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader* Loader::loader(const QString& name)
|
||||||
|
{
|
||||||
|
foreach (Loader* l, _loaderList)
|
||||||
|
if (l->name() == name)
|
||||||
|
return l;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// factories of available loaders
|
||||||
|
Loader* createCachegrindLoader();
|
||||||
|
|
||||||
|
void Loader::initLoaders()
|
||||||
|
{
|
||||||
|
_loaderList.append(createCachegrindLoader());
|
||||||
|
//_loaderList.append(GProfLoader::createLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Loader::deleteLoaders()
|
||||||
|
{
|
||||||
|
while (!_loaderList.isEmpty())
|
||||||
|
delete _loaderList.takeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
// notifications
|
||||||
|
|
||||||
|
void Loader::setLogger(Logger* l)
|
||||||
|
{
|
||||||
|
_logger = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Loader::loadStart(const QString& filename)
|
||||||
|
{
|
||||||
|
if (_logger)
|
||||||
|
_logger->loadStart(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Loader::loadProgress(int progress)
|
||||||
|
{
|
||||||
|
if (_logger)
|
||||||
|
_logger->loadProgress(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Loader::loadError(int line, const QString& msg)
|
||||||
|
{
|
||||||
|
if (_logger)
|
||||||
|
_logger->loadError(line, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Loader::loadWarning(int line, const QString& msg)
|
||||||
|
{
|
||||||
|
if (_logger)
|
||||||
|
_logger->loadWarning(line, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Loader::loadFinished(const QString& msg)
|
||||||
|
{
|
||||||
|
if (_logger)
|
||||||
|
_logger->loadFinished(msg);
|
||||||
|
}
|
||||||
|
|
94
kcachegrind/libcore/loader.h
Normal file
94
kcachegrind/libcore/loader.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002 - 2010 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Base class for loaders of profiling data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LOADER_H
|
||||||
|
#define LOADER_H
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class QIODevice;
|
||||||
|
class TraceData;
|
||||||
|
class Loader;
|
||||||
|
class Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To implement a new loader, inherit from the Loader class and
|
||||||
|
* and reimplement canLoad() and load().
|
||||||
|
*
|
||||||
|
* For registration, put into the static initLoaders() function
|
||||||
|
* of this base class a _loaderList.append(new MyLoader()).
|
||||||
|
*
|
||||||
|
* matchingLoader() returns the first loader able to load a file.
|
||||||
|
*
|
||||||
|
* To show progress and warnings while loading,
|
||||||
|
* loadStatus(), loadError() and loadWarning() should be called.
|
||||||
|
* These are just shown as status, warnings or errors to the
|
||||||
|
* user, but do not show real failure, as even errors can be
|
||||||
|
* recoverable. For unablility to load a file, return 0 in
|
||||||
|
* load().
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Loader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Loader(const QString& name, const QString& desc);
|
||||||
|
virtual ~Loader();
|
||||||
|
|
||||||
|
// reimplement for a specific Loader
|
||||||
|
virtual bool canLoad(QIODevice* file);
|
||||||
|
/* load a profile data file.
|
||||||
|
* for every section (time span covered by profile), create a TracePart
|
||||||
|
* return the number of sections loaded (0 on error)
|
||||||
|
*/
|
||||||
|
virtual int load(TraceData*, QIODevice* file, const QString& filename);
|
||||||
|
|
||||||
|
static Loader* matchingLoader(QIODevice* file);
|
||||||
|
static Loader* loader(const QString& name);
|
||||||
|
static void initLoaders();
|
||||||
|
static void deleteLoaders();
|
||||||
|
|
||||||
|
QString name() const { return _name; }
|
||||||
|
QString description() const { return _description; }
|
||||||
|
|
||||||
|
// consumer for notifications
|
||||||
|
void setLogger(Logger*);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// notifications for the user
|
||||||
|
void loadStart(const QString& filename);
|
||||||
|
void loadProgress(int progress); // 0 - 100
|
||||||
|
void loadError(int line, const QString& msg);
|
||||||
|
void loadWarning(int line, const QString& msg);
|
||||||
|
void loadFinished(const QString &msg = QString::null);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Logger* _logger;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _name, _description;
|
||||||
|
|
||||||
|
static QList<Loader*> _loaderList;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
69
kcachegrind/libcore/logger.cpp
Normal file
69
kcachegrind/libcore/logger.cpp
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2008 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default implementation for notification dispatcher: use qDebug
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
|
||||||
|
/// Logger
|
||||||
|
|
||||||
|
Logger::~Logger()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Logger::loadStart(const QString& filename)
|
||||||
|
{
|
||||||
|
_filename = filename;
|
||||||
|
_timer.setSingleShot(true);
|
||||||
|
_timer.start(1000);
|
||||||
|
qDebug() << "Loading" << filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::loadProgress(int progress)
|
||||||
|
{
|
||||||
|
// print progress at most every second
|
||||||
|
if (_timer.isActive()) return;
|
||||||
|
_timer.start(1000);
|
||||||
|
|
||||||
|
qDebug() << "Loading" << _filename << "(" << progress << "%)";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::loadWarning(int line, const QString& msg)
|
||||||
|
{
|
||||||
|
qDebug() << "Warning in " << _filename << ", line" << line
|
||||||
|
<< ":" << msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::loadError(int line, const QString& msg)
|
||||||
|
{
|
||||||
|
qDebug() << "Error in " << _filename << ", line" << line
|
||||||
|
<< ":" << msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::loadFinished(const QString& msg)
|
||||||
|
{
|
||||||
|
_timer.stop();
|
||||||
|
if (msg.isEmpty())
|
||||||
|
qDebug() << "File" << _filename << "loaded.";
|
||||||
|
else
|
||||||
|
qDebug() << "Error loading file" << _filename << ":" << qPrintable(msg);
|
||||||
|
}
|
53
kcachegrind/libcore/logger.h
Normal file
53
kcachegrind/libcore/logger.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002-2008 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dispatcher for messages (status/warning/errors) from libcore
|
||||||
|
*
|
||||||
|
* For different front ends which want to present the messages to the
|
||||||
|
* user, a derived class should be implemented
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LOGGER_H
|
||||||
|
#define LOGGER_H
|
||||||
|
|
||||||
|
#include <qstring.h>
|
||||||
|
#include <qtimer.h>
|
||||||
|
|
||||||
|
class Logger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Logger();
|
||||||
|
|
||||||
|
// Notifications for file loading
|
||||||
|
virtual void loadStart(const QString& filename);
|
||||||
|
virtual void loadProgress(int progress); // 0 - 100
|
||||||
|
virtual void loadWarning(int line, const QString& msg);
|
||||||
|
virtual void loadError(int line, const QString& msg);
|
||||||
|
virtual void loadFinished(const QString& msg); // msg could be error
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString _filename;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTimer _timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LOGGER_H
|
||||||
|
|
||||||
|
|
264
kcachegrind/libcore/pool.cpp
Normal file
264
kcachegrind/libcore/pool.cpp
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002-2004 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <qglobal.h>
|
||||||
|
#include "pool.h"
|
||||||
|
|
||||||
|
// FixPool
|
||||||
|
|
||||||
|
#define CHUNK_SIZE 100000
|
||||||
|
|
||||||
|
struct SpaceChunk
|
||||||
|
{
|
||||||
|
struct SpaceChunk* next;
|
||||||
|
unsigned int used;
|
||||||
|
char space[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
FixPool::FixPool()
|
||||||
|
{
|
||||||
|
_first = _last = 0;
|
||||||
|
_reservation = 0;
|
||||||
|
_count = 0;
|
||||||
|
_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FixPool::~FixPool()
|
||||||
|
{
|
||||||
|
struct SpaceChunk* chunk = _first, *next;
|
||||||
|
|
||||||
|
while(chunk) {
|
||||||
|
next = chunk->next;
|
||||||
|
free(chunk);
|
||||||
|
chunk = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0) qDebug("~FixPool: Had %d objects with total size %d\n",
|
||||||
|
_count, _size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* FixPool::allocate(unsigned int size)
|
||||||
|
{
|
||||||
|
if (!ensureSpace(size)) return 0;
|
||||||
|
|
||||||
|
_reservation = 0;
|
||||||
|
void* result = _last->space + _last->used;
|
||||||
|
_last->used += size;
|
||||||
|
|
||||||
|
_count++;
|
||||||
|
_size += size;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* FixPool::reserve(unsigned int size)
|
||||||
|
{
|
||||||
|
if (!ensureSpace(size)) return 0;
|
||||||
|
_reservation = size;
|
||||||
|
|
||||||
|
return _last->space + _last->used;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool FixPool::allocateReserved(unsigned int size)
|
||||||
|
{
|
||||||
|
if (_reservation < size) return false;
|
||||||
|
|
||||||
|
_reservation = 0;
|
||||||
|
_last->used += size;
|
||||||
|
|
||||||
|
_count++;
|
||||||
|
_size += size;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FixPool::ensureSpace(unsigned int size)
|
||||||
|
{
|
||||||
|
if (_last && _last->used + size <= CHUNK_SIZE) return true;
|
||||||
|
|
||||||
|
struct SpaceChunk* newChunk;
|
||||||
|
|
||||||
|
// we do not allow allocation sizes > CHUNK_SIZE
|
||||||
|
if (size > CHUNK_SIZE) return false;
|
||||||
|
|
||||||
|
newChunk = (struct SpaceChunk*) malloc(sizeof(struct SpaceChunk) +
|
||||||
|
CHUNK_SIZE);
|
||||||
|
if (!newChunk) {
|
||||||
|
qFatal("ERROR: Out of memory. Sorry. KCachegrind has to terminate.\n\n"
|
||||||
|
"You probably tried to load a profile data file too huge for"
|
||||||
|
"this system. You could try loading this file on a 64-bit OS.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
newChunk->next = 0;
|
||||||
|
newChunk->used = 0;
|
||||||
|
|
||||||
|
if (!_last) {
|
||||||
|
_last = _first = newChunk;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_last->next = newChunk;
|
||||||
|
_last = newChunk;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// DynPool
|
||||||
|
|
||||||
|
DynPool::DynPool()
|
||||||
|
{
|
||||||
|
_data = (char*) malloc(CHUNK_SIZE);
|
||||||
|
_used = 0;
|
||||||
|
_size = CHUNK_SIZE;
|
||||||
|
|
||||||
|
// end marker
|
||||||
|
*(int*)_data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynPool::~DynPool()
|
||||||
|
{
|
||||||
|
// we could check for correctness by iteration over all objects
|
||||||
|
|
||||||
|
::free(_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DynPool::allocate(char** ptr, unsigned int size)
|
||||||
|
{
|
||||||
|
// round up to multiple of 4
|
||||||
|
size = (size+3) & ~3;
|
||||||
|
|
||||||
|
/* need 12 bytes more:
|
||||||
|
* - 4 bytes for forward chain
|
||||||
|
* - 4 bytes for pointer to ptr
|
||||||
|
* - 4 bytes as end marker (not used for new object)
|
||||||
|
*/
|
||||||
|
if (!ensureSpace(size + 12)) return false;
|
||||||
|
|
||||||
|
char** obj = (char**) (_data+_used);
|
||||||
|
obj[0] = (char*)(_data + _used + size + 8);
|
||||||
|
obj[1] = (char*)ptr;
|
||||||
|
*(int*)(_data+_used+size+8) = 0;
|
||||||
|
*ptr = _data+_used+8;
|
||||||
|
|
||||||
|
_used += size + 8;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DynPool::free(char** ptr)
|
||||||
|
{
|
||||||
|
if (!ptr ||
|
||||||
|
!*ptr ||
|
||||||
|
(*(char**)(*ptr - 4)) != (char*)ptr )
|
||||||
|
qFatal("Chaining error in DynPool::free");
|
||||||
|
|
||||||
|
(*(char**)(*ptr - 4)) = 0;
|
||||||
|
*ptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DynPool::ensureSpace(unsigned int size)
|
||||||
|
{
|
||||||
|
if (_used + size <= _size) return true;
|
||||||
|
|
||||||
|
unsigned int newsize = _size *3/2 + CHUNK_SIZE;
|
||||||
|
char* newdata = (char*) malloc(newsize);
|
||||||
|
|
||||||
|
unsigned int freed = 0, len;
|
||||||
|
char **p, **pnext, **pnew;
|
||||||
|
|
||||||
|
qDebug("DynPool::ensureSpace size: %d => %d, used %d. %p => %p",
|
||||||
|
_size, newsize, _used, _data, newdata);
|
||||||
|
|
||||||
|
pnew = (char**) newdata;
|
||||||
|
p = (char**) _data;
|
||||||
|
while(*p) {
|
||||||
|
pnext = (char**) *p;
|
||||||
|
len = (char*)pnext - (char*)p;
|
||||||
|
|
||||||
|
if (0) qDebug(" [%8p] Len %d (ptr %p), freed %d (=> %p)",
|
||||||
|
p, len, p[1], freed, pnew);
|
||||||
|
|
||||||
|
/* skip freed space ? */
|
||||||
|
if (p[1] == 0) {
|
||||||
|
freed += len;
|
||||||
|
p = pnext;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// new and old still at same address ?
|
||||||
|
if (pnew == p) {
|
||||||
|
pnew = p = pnext;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy object
|
||||||
|
pnew[0] = (char*)pnew + len;
|
||||||
|
pnew[1] = p[1];
|
||||||
|
memcpy((char*)pnew + 8, (char*)p + 8, len-8);
|
||||||
|
|
||||||
|
// update pointer to object
|
||||||
|
char** ptr = (char**) p[1];
|
||||||
|
if (*ptr != ((char*)p)+8)
|
||||||
|
qFatal("Chaining error in DynPool::ensureSpace");
|
||||||
|
*ptr = ((char*)pnew)+8;
|
||||||
|
|
||||||
|
pnew = (char**) pnew[0];
|
||||||
|
p = pnext;
|
||||||
|
}
|
||||||
|
pnew[0] = 0;
|
||||||
|
|
||||||
|
unsigned int newused = (char*)pnew - (char*)newdata;
|
||||||
|
qDebug("DynPool::ensureSpace size: %d => %d, used %d => %d (%d freed)",
|
||||||
|
_size, newsize, _used, newused, freed);
|
||||||
|
|
||||||
|
::free(_data);
|
||||||
|
_data = newdata;
|
||||||
|
_size = newsize;
|
||||||
|
_used = newused;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Testing the DynPool
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char* bufs[CHUNK_SIZE];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
DynPool p;
|
||||||
|
|
||||||
|
for(i=0;i<CHUNK_SIZE;i++) {
|
||||||
|
p.allocate(bufs+i, 10+i%10);
|
||||||
|
if (((i%3)==0) && (i>20))
|
||||||
|
p.free(bufs+i-20);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0;i<CHUNK_SIZE;i++) {
|
||||||
|
if ((bufs[i]==0) || ((i%7)==0)) continue;
|
||||||
|
p.free(bufs+i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0;i<CHUNK_SIZE;i++) {
|
||||||
|
if (bufs[i]) continue;
|
||||||
|
p.allocate(bufs+i, 10+i%10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
107
kcachegrind/libcore/pool.h
Normal file
107
kcachegrind/libcore/pool.h
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002-2004 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef POOL_H
|
||||||
|
#define POOL_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pool objects: containers for many small objects.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct SpaceChunk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FixPool
|
||||||
|
*
|
||||||
|
* For objects with fixed size and life time
|
||||||
|
* ending with that of the pool.
|
||||||
|
*/
|
||||||
|
class FixPool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FixPool();
|
||||||
|
~FixPool();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take <size> bytes from the pool
|
||||||
|
*/
|
||||||
|
void* allocate(unsigned int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserve space. If you call allocateReservedSpace(realsize)
|
||||||
|
* with realSize < reserved size directly after, you
|
||||||
|
* will get the same memory area.
|
||||||
|
*/
|
||||||
|
void* reserve(unsigned int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Before calling this, you have to reserve at least <size> bytes
|
||||||
|
* with reserveSpace().
|
||||||
|
*/
|
||||||
|
bool allocateReserved(unsigned int size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* Checks that there is enough space in the last chunk.
|
||||||
|
* Returns false if this is not possible.
|
||||||
|
*/
|
||||||
|
bool ensureSpace(unsigned int);
|
||||||
|
|
||||||
|
struct SpaceChunk *_first, *_last;
|
||||||
|
unsigned int _reservation;
|
||||||
|
int _count, _size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DynPool
|
||||||
|
*
|
||||||
|
* For objects which probably need to be resized
|
||||||
|
* in the future. Objects also can be deleted to free up space.
|
||||||
|
* As objects can also be moved in a defragmentation step,
|
||||||
|
* access has to be done via the given pointer object.
|
||||||
|
*/
|
||||||
|
class DynPool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DynPool();
|
||||||
|
~DynPool();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take <size> bytes from the pool, changing <*ptr>
|
||||||
|
* to point to this allocated space.
|
||||||
|
* <*ptr> will be changed if the object is moved.
|
||||||
|
* Returns false if no space available.
|
||||||
|
*/
|
||||||
|
bool allocate(char** ptr, unsigned int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To resize, first allocate new space, and free old
|
||||||
|
* afterwards.
|
||||||
|
*/
|
||||||
|
void free(char** ptr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* Checks that there is enough space. If not,
|
||||||
|
* it compactifies, possibly moving objects.
|
||||||
|
*/
|
||||||
|
bool ensureSpace(unsigned int);
|
||||||
|
|
||||||
|
char* _data;
|
||||||
|
unsigned int _used, _size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // POOL_H
|
399
kcachegrind/libcore/stackbrowser.cpp
Normal file
399
kcachegrind/libcore/stackbrowser.cpp
Normal file
|
@ -0,0 +1,399 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "stackbrowser.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Stack
|
||||||
|
|
||||||
|
Stack::Stack(TraceFunction* top, TraceCallList calls)
|
||||||
|
{
|
||||||
|
_refCount = 0;
|
||||||
|
_top = top;
|
||||||
|
_calls = calls;
|
||||||
|
|
||||||
|
extendBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stack::Stack(TraceFunction* f)
|
||||||
|
{
|
||||||
|
_refCount = 0;
|
||||||
|
_top = f;
|
||||||
|
|
||||||
|
extendBottom();
|
||||||
|
extendTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stack::extendBottom()
|
||||||
|
{
|
||||||
|
SubCost most;
|
||||||
|
TraceFunction* f;
|
||||||
|
|
||||||
|
if (!_calls.isEmpty())
|
||||||
|
f = _calls.last()->called();
|
||||||
|
else
|
||||||
|
f = _top;
|
||||||
|
|
||||||
|
if (!f) return;
|
||||||
|
// do not follow calls from cycles
|
||||||
|
if (f->cycle() == f) return;
|
||||||
|
|
||||||
|
// event type to use for the "most probable" call stack
|
||||||
|
// we simply take the first real event type
|
||||||
|
if ((_top->data() == 0) ||
|
||||||
|
(_top->data()->eventTypes()->realCount() <1)) return;
|
||||||
|
EventType* e = _top->data()->eventTypes()->realType(0);
|
||||||
|
|
||||||
|
int max = 30;
|
||||||
|
|
||||||
|
// try to extend to lower stack frames
|
||||||
|
while (f && (max-- >0)) {
|
||||||
|
TraceCall* call = 0;
|
||||||
|
most = 0;
|
||||||
|
foreach(TraceCall* c, f->callings()) {
|
||||||
|
// no cycle calls in stack: could be deleted without notice
|
||||||
|
if (c->called()->cycle() == c->called()) continue;
|
||||||
|
// no simple recursions
|
||||||
|
if (c->called() == _top) continue;
|
||||||
|
|
||||||
|
if (c->called()->name().isEmpty()) continue;
|
||||||
|
SubCost sc = c->subCost(e);
|
||||||
|
if (sc == 0) continue;
|
||||||
|
|
||||||
|
if (sc > most) {
|
||||||
|
most = sc;
|
||||||
|
call = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!call)
|
||||||
|
break;
|
||||||
|
|
||||||
|
_calls.append(call);
|
||||||
|
f = call->called();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Stack::extendTop()
|
||||||
|
{
|
||||||
|
SubCost most;
|
||||||
|
|
||||||
|
int max = 10;
|
||||||
|
|
||||||
|
// do not follow calls from cycles
|
||||||
|
if (_top->cycle() == _top) return;
|
||||||
|
|
||||||
|
// event type to use for the "most probable" call stack
|
||||||
|
// we simply take the first real event type
|
||||||
|
if ((_top->data() == 0) ||
|
||||||
|
(_top->data()->eventTypes()->realCount() <1)) return;
|
||||||
|
EventType* e = _top->data()->eventTypes()->realType(0);
|
||||||
|
|
||||||
|
// try to extend to upper stack frames
|
||||||
|
while (_top && (max-- >0)) {
|
||||||
|
TraceCall* call = 0;
|
||||||
|
most = 0;
|
||||||
|
foreach(TraceCall* c, _top->callers()) {
|
||||||
|
// no cycle calls in stack: could be deleted without notice
|
||||||
|
if (c->caller()->cycle() == c->caller()) continue;
|
||||||
|
// no simple recursions
|
||||||
|
if (c->caller() == _top) continue;
|
||||||
|
|
||||||
|
if (c->caller()->name().isEmpty()) continue;
|
||||||
|
SubCost sc = c->subCost(e);
|
||||||
|
if (sc == 0) continue;
|
||||||
|
|
||||||
|
if (sc > most) {
|
||||||
|
most = sc;
|
||||||
|
call = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!call)
|
||||||
|
break;
|
||||||
|
|
||||||
|
_calls.prepend(call);
|
||||||
|
_top = call->caller();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceFunction* Stack::caller(TraceFunction* fn, bool extend)
|
||||||
|
{
|
||||||
|
TraceFunction* f;
|
||||||
|
|
||||||
|
if (extend && (_top == fn)) {
|
||||||
|
// extend at top
|
||||||
|
extendTop();
|
||||||
|
f = _top;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(TraceCall* c, _calls) {
|
||||||
|
f = c->called();
|
||||||
|
if (f == fn)
|
||||||
|
return c->caller();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceFunction* Stack::called(TraceFunction* fn, bool extend)
|
||||||
|
{
|
||||||
|
TraceFunction* f;
|
||||||
|
|
||||||
|
foreach(TraceCall* c, _calls) {
|
||||||
|
f = c->caller();
|
||||||
|
if (f == fn)
|
||||||
|
return c->called();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extend) {
|
||||||
|
// extend at bottom
|
||||||
|
extendBottom();
|
||||||
|
|
||||||
|
// and search again
|
||||||
|
foreach(TraceCall* c, _calls) {
|
||||||
|
f = c->caller();
|
||||||
|
if (f == fn)
|
||||||
|
return c->called();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Stack::contains(TraceFunction* fn)
|
||||||
|
{
|
||||||
|
// cycles are listed on there own
|
||||||
|
if (fn->cycle() == fn) return false;
|
||||||
|
if (_top->cycle() == _top) return false;
|
||||||
|
|
||||||
|
if (fn == _top)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
TraceFunction* f = _top;
|
||||||
|
|
||||||
|
foreach(TraceCall* c, _calls) {
|
||||||
|
f = c->called();
|
||||||
|
if (f == fn)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to extend at bottom (even if callCount 0)
|
||||||
|
foreach(TraceCall* c, f->callings()) {
|
||||||
|
f = c->called();
|
||||||
|
if (f == fn) {
|
||||||
|
_calls.append(c);
|
||||||
|
|
||||||
|
// extend at bottom after found one
|
||||||
|
extendBottom();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to extend at top (even if callCount 0)
|
||||||
|
foreach(TraceCall* c, _top->callers()) {
|
||||||
|
f = c->caller();
|
||||||
|
if (f == fn) {
|
||||||
|
_calls.prepend(c);
|
||||||
|
|
||||||
|
// extend at top after found one
|
||||||
|
extendTop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stack* Stack::split(TraceFunction* f)
|
||||||
|
{
|
||||||
|
TraceCallList calls = _calls;
|
||||||
|
|
||||||
|
// cycles are listed on there own
|
||||||
|
if (f->cycle() == f) return 0;
|
||||||
|
if (_top->cycle() == _top) return 0;
|
||||||
|
|
||||||
|
foreach(TraceCall* c, calls) {
|
||||||
|
foreach(TraceCall* c2, c->called()->callings()) {
|
||||||
|
if (c2 == c) continue;
|
||||||
|
if (c2->called() != f) continue;
|
||||||
|
|
||||||
|
// remove bottom part
|
||||||
|
while (!calls.isEmpty() && (calls.last()!=c))
|
||||||
|
calls.removeLast();
|
||||||
|
|
||||||
|
calls.append(c2);
|
||||||
|
return new Stack(_top, calls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Stack::toString()
|
||||||
|
{
|
||||||
|
QString res = _top->name();
|
||||||
|
foreach(TraceCall *c, _calls)
|
||||||
|
res += "\n > " + c->called()->name();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// HistoryItem
|
||||||
|
|
||||||
|
HistoryItem::HistoryItem(Stack* stack, TraceFunction* function)
|
||||||
|
{
|
||||||
|
_stack = stack;
|
||||||
|
_function = function;
|
||||||
|
if (_stack)
|
||||||
|
_stack->ref();
|
||||||
|
|
||||||
|
_last = 0;
|
||||||
|
_next = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
qDebug("New Stack History Item (sRef %d): %s\n %s",
|
||||||
|
_stack->refCount(), qPrintable(_function->name()),
|
||||||
|
qPrintable(_stack->toString()));
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryItem::~HistoryItem()
|
||||||
|
{
|
||||||
|
if (0) qDebug("Deleting Stack History Item (sRef %d): %s",
|
||||||
|
_stack->refCount(),
|
||||||
|
qPrintable(_function->name()));
|
||||||
|
|
||||||
|
if (_last)
|
||||||
|
_last->_next = _next;
|
||||||
|
if (_stack) {
|
||||||
|
if (_stack->deref() == 0)
|
||||||
|
delete _stack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// StackBrowser
|
||||||
|
|
||||||
|
StackBrowser::StackBrowser()
|
||||||
|
{
|
||||||
|
_current = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
StackBrowser::~StackBrowser()
|
||||||
|
{
|
||||||
|
delete _current;
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryItem* StackBrowser::select(TraceFunction* f)
|
||||||
|
{
|
||||||
|
if (!_current) {
|
||||||
|
Stack* s = new Stack(f);
|
||||||
|
_current = new HistoryItem(s, f);
|
||||||
|
}
|
||||||
|
else if (_current->function() != f) {
|
||||||
|
// make current item the last one
|
||||||
|
HistoryItem* item = _current;
|
||||||
|
if (item->next()) {
|
||||||
|
item = item->next();
|
||||||
|
item->last()->setNext(0);
|
||||||
|
|
||||||
|
while (item->next()) {
|
||||||
|
item = item->next();
|
||||||
|
delete item->last();
|
||||||
|
}
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stack* s = _current->stack();
|
||||||
|
if (!s->contains(f)) {
|
||||||
|
s = s->split(f);
|
||||||
|
if (!s)
|
||||||
|
s = new Stack(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
item = _current;
|
||||||
|
_current = new HistoryItem(s, f);
|
||||||
|
item->setNext(_current);
|
||||||
|
_current->setLast(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// qDebug("Selected %s in StackBrowser", qPrintable(f->name()));
|
||||||
|
|
||||||
|
return _current;
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryItem* StackBrowser::goBack()
|
||||||
|
{
|
||||||
|
if (_current && _current->last())
|
||||||
|
_current = _current->last();
|
||||||
|
|
||||||
|
return _current;
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryItem* StackBrowser::goForward()
|
||||||
|
{
|
||||||
|
if (_current && _current->next())
|
||||||
|
_current = _current->next();
|
||||||
|
|
||||||
|
return _current;
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryItem* StackBrowser::goUp()
|
||||||
|
{
|
||||||
|
if (_current) {
|
||||||
|
TraceFunction* f = _current->stack()->caller(_current->function(), true);
|
||||||
|
if (f)
|
||||||
|
_current = select(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _current;
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryItem* StackBrowser::goDown()
|
||||||
|
{
|
||||||
|
if (_current) {
|
||||||
|
TraceFunction* f = _current->stack()->called(_current->function(), true);
|
||||||
|
if (f)
|
||||||
|
_current = select(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _current;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StackBrowser::canGoBack()
|
||||||
|
{
|
||||||
|
return _current && _current->last();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StackBrowser::canGoForward()
|
||||||
|
{
|
||||||
|
return _current && _current->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StackBrowser::canGoUp()
|
||||||
|
{
|
||||||
|
if (!_current) return false;
|
||||||
|
|
||||||
|
return _current->stack()->caller(_current->function(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StackBrowser::canGoDown()
|
||||||
|
{
|
||||||
|
if (!_current) return false;
|
||||||
|
|
||||||
|
return _current->stack()->called(_current->function(), false);
|
||||||
|
}
|
109
kcachegrind/libcore/stackbrowser.h
Normal file
109
kcachegrind/libcore/stackbrowser.h
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STACKBROWSER_H
|
||||||
|
#define STACKBROWSER_H
|
||||||
|
|
||||||
|
#include "tracedata.h"
|
||||||
|
|
||||||
|
// A history of selected functions within stacks
|
||||||
|
|
||||||
|
class Stack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Stack(TraceFunction*);
|
||||||
|
|
||||||
|
// extend the stack at top/bottom if possible
|
||||||
|
bool contains(TraceFunction*);
|
||||||
|
|
||||||
|
void extendBottom();
|
||||||
|
void extendTop();
|
||||||
|
|
||||||
|
// search for a function on stack calling specified function.
|
||||||
|
// if found, return upper part with new function call
|
||||||
|
Stack* split(TraceFunction*);
|
||||||
|
|
||||||
|
// increment reference count
|
||||||
|
void ref() { _refCount++; }
|
||||||
|
// decrement reference count
|
||||||
|
bool deref() { return --_refCount; }
|
||||||
|
int refCount() const { return _refCount; }
|
||||||
|
|
||||||
|
TraceFunction* top() { return _top; }
|
||||||
|
TraceCallList calls() { return _calls; }
|
||||||
|
TraceFunction* caller(TraceFunction*, bool extend);
|
||||||
|
TraceFunction* called(TraceFunction*, bool extend);
|
||||||
|
|
||||||
|
QString toString();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Stack(TraceFunction* top, TraceCallList list);
|
||||||
|
|
||||||
|
// at the top of the stack we have a function...
|
||||||
|
TraceFunction* _top;
|
||||||
|
// list ordered from top to bottom
|
||||||
|
TraceCallList _calls;
|
||||||
|
int _refCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HistoryItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HistoryItem(Stack*, TraceFunction*);
|
||||||
|
~HistoryItem();
|
||||||
|
|
||||||
|
Stack* stack() { return _stack; }
|
||||||
|
TraceFunction* function() { return _function; }
|
||||||
|
HistoryItem* last() { return _last; }
|
||||||
|
HistoryItem* next() { return _next; }
|
||||||
|
void setLast(HistoryItem* h) { _last = h; }
|
||||||
|
void setNext(HistoryItem* h) { _next = h; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
HistoryItem *_last, *_next;
|
||||||
|
Stack* _stack;
|
||||||
|
TraceFunction* _function;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class StackBrowser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StackBrowser();
|
||||||
|
~StackBrowser();
|
||||||
|
|
||||||
|
// A function was selected. This creates a new history entry
|
||||||
|
HistoryItem* select(TraceFunction*);
|
||||||
|
|
||||||
|
HistoryItem* current() { return _current; }
|
||||||
|
bool canGoBack();
|
||||||
|
bool canGoForward();
|
||||||
|
bool canGoUp();
|
||||||
|
bool canGoDown();
|
||||||
|
HistoryItem* goBack();
|
||||||
|
HistoryItem* goForward();
|
||||||
|
HistoryItem* goUp();
|
||||||
|
HistoryItem* goDown();
|
||||||
|
|
||||||
|
private:
|
||||||
|
HistoryItem* _current;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
107
kcachegrind/libcore/subcost.cpp
Normal file
107
kcachegrind/libcore/subcost.cpp
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2004 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "subcost.h"
|
||||||
|
#include "tracedata.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------
|
||||||
|
// SubCost
|
||||||
|
|
||||||
|
bool SubCost::set(const char** ps)
|
||||||
|
{
|
||||||
|
const char* s = *ps;
|
||||||
|
if (!s || (*s < '0') || (*s > '9')) return false;
|
||||||
|
|
||||||
|
v = *s - '0';
|
||||||
|
s++;
|
||||||
|
while(*s >= '0' && *s <= '9') {
|
||||||
|
v = 10* v + (*s-'0');
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
while(*s == ' ') s++;
|
||||||
|
*ps = s;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SubCost::pretty(char sep) const
|
||||||
|
{
|
||||||
|
unsigned long long n = v;
|
||||||
|
|
||||||
|
if (n==0) return QString("0");
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
QString res = "";
|
||||||
|
|
||||||
|
while (n) {
|
||||||
|
if ((i>0) && !(i%3)) res = sep + res;
|
||||||
|
i++;
|
||||||
|
res = QChar('0'+int(n%10)) + res;
|
||||||
|
n /= 10;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// HighestCostList
|
||||||
|
|
||||||
|
HighestCostList::HighestCostList()
|
||||||
|
{
|
||||||
|
_costType = 0;
|
||||||
|
clear(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighestCostList::clear(int maxSize)
|
||||||
|
{
|
||||||
|
_maxSize = maxSize;
|
||||||
|
_count = 0;
|
||||||
|
_item.resize(maxSize);
|
||||||
|
_cost.resize(maxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighestCostList::addCost(ProfileCostArray* c, SubCost cost)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (_maxSize == 0) return;
|
||||||
|
|
||||||
|
_count++;
|
||||||
|
if (_count > _maxSize) {
|
||||||
|
if (_cost[_maxSize-1] >= cost) return;
|
||||||
|
i = _maxSize-1;
|
||||||
|
}
|
||||||
|
else i = _count-1;
|
||||||
|
|
||||||
|
for(; i>0; i--) {
|
||||||
|
if (_cost[i-1] >= cost) break;
|
||||||
|
else {
|
||||||
|
_cost[i] = _cost[i-1];
|
||||||
|
_item[i] = _item[i-1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_cost[i] = cost;
|
||||||
|
_item[i] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
102
kcachegrind/libcore/subcost.h
Normal file
102
kcachegrind/libcore/subcost.h
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002-2004 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SUBCOST_H
|
||||||
|
#define SUBCOST_H
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
typedef unsigned long long uint64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cost event counter, simple wrapper around a 64bit entity
|
||||||
|
*/
|
||||||
|
class SubCost
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SubCost() { v=0; }
|
||||||
|
SubCost(uint64 i) { v=i; }
|
||||||
|
SubCost(unsigned i) { v=i; }
|
||||||
|
SubCost(int i) { v=(unsigned)i; }
|
||||||
|
SubCost(double d) { v= (uint64)(d + .5); }
|
||||||
|
|
||||||
|
SubCost& operator=(uint64 i) { v = i; return *this; }
|
||||||
|
SubCost& operator=(unsigned i) { v = i; return *this; }
|
||||||
|
SubCost& operator=(int i) { v = i; return *this; }
|
||||||
|
SubCost& operator=(double d) { v = (uint64)(d + .5); return *this; }
|
||||||
|
|
||||||
|
bool set(const char** s);
|
||||||
|
bool set(FixString& s) { return s.stripUInt64(v); }
|
||||||
|
|
||||||
|
operator uint64&() { return v; }
|
||||||
|
operator uint64() const { return v; }
|
||||||
|
|
||||||
|
bool operator==(unsigned i) const { return v == i; }
|
||||||
|
bool operator==(int i) const { return v == (unsigned)i; }
|
||||||
|
bool operator<(unsigned i) const { return v < i; }
|
||||||
|
bool operator<(int i) const { return v < (unsigned)i; }
|
||||||
|
bool operator<(const SubCost& s) const { return v < s.v; }
|
||||||
|
bool operator>(unsigned i) const { return v > i; }
|
||||||
|
bool operator>(int i) const { return v > (unsigned)i; }
|
||||||
|
bool operator>(const SubCost& s) const { return v > s.v; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert SubCost value into a QString,
|
||||||
|
* spaced every 3 digits.
|
||||||
|
*/
|
||||||
|
QString pretty(char sep = ' ') const;
|
||||||
|
|
||||||
|
uint64 v;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProfileCostArray;
|
||||||
|
class EventType;
|
||||||
|
typedef QList<ProfileCostArray*> TraceCostList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to calculate the <maxSize> ProfileCostArray items
|
||||||
|
* with highest cost.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class HighestCostList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighestCostList();
|
||||||
|
|
||||||
|
void clear(int maxSize);
|
||||||
|
void addCost(ProfileCostArray*, SubCost);
|
||||||
|
int count() { return _count; }
|
||||||
|
int realCount() { return (_count > _maxSize) ? _maxSize:_count; }
|
||||||
|
int maxSize() { return _maxSize; }
|
||||||
|
bool hasMore() { return _count > _maxSize; }
|
||||||
|
ProfileCostArray* operator[] (int i)
|
||||||
|
{ return (i>=0 && i<_count && i<_maxSize) ? _item[i] : 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
TraceCostList _list;
|
||||||
|
int _maxSize, _count;
|
||||||
|
EventType* _costType;
|
||||||
|
QVector<ProfileCostArray*> _item;
|
||||||
|
QVector<SubCost> _cost;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
3744
kcachegrind/libcore/tracedata.cpp
Normal file
3744
kcachegrind/libcore/tracedata.cpp
Normal file
File diff suppressed because it is too large
Load diff
1510
kcachegrind/libcore/tracedata.h
Normal file
1510
kcachegrind/libcore/tracedata.h
Normal file
File diff suppressed because it is too large
Load diff
490
kcachegrind/libcore/utils.cpp
Normal file
490
kcachegrind/libcore/utils.cpp
Normal file
|
@ -0,0 +1,490 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility classes for KCachegrind
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <QIODevice>
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// class FixString
|
||||||
|
|
||||||
|
FixString::FixString(const char* str, int len)
|
||||||
|
{
|
||||||
|
_str = str;
|
||||||
|
_len = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FixString::stripFirst(char& c)
|
||||||
|
{
|
||||||
|
if (!_len) {
|
||||||
|
c = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = *_str;
|
||||||
|
_str++;
|
||||||
|
_len--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FixString::stripPrefix(const char* p)
|
||||||
|
{
|
||||||
|
if (_len == 0) return false;
|
||||||
|
if (!p || (*p != *_str)) return false;
|
||||||
|
|
||||||
|
const char* s = _str+1;
|
||||||
|
int l = _len-1;
|
||||||
|
p++;
|
||||||
|
while(*p) {
|
||||||
|
if (l==0) return false;
|
||||||
|
if (*s != *p) return false;
|
||||||
|
p++;
|
||||||
|
s++;
|
||||||
|
l--;
|
||||||
|
}
|
||||||
|
_str = s;
|
||||||
|
_len = l;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// this parses hexadecimal (with prefix '0x' too)
|
||||||
|
bool FixString::stripUInt(unsigned int& v, bool stripSpaces)
|
||||||
|
{
|
||||||
|
if (_len==0) {
|
||||||
|
v = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char c = *_str;
|
||||||
|
if (c<'0' || c>'9') {
|
||||||
|
v = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = c-'0';
|
||||||
|
const char* s = _str+1;
|
||||||
|
int l = _len-1;
|
||||||
|
c = *s;
|
||||||
|
|
||||||
|
if ((l>0) && (c == 'x') && (v==0)) {
|
||||||
|
// hexadecimal
|
||||||
|
s++;
|
||||||
|
c = *s;
|
||||||
|
l--;
|
||||||
|
|
||||||
|
while(l>0) {
|
||||||
|
if (c>='0' && c<='9')
|
||||||
|
v = 16*v + (c-'0');
|
||||||
|
else if (c>='a' && c<='f')
|
||||||
|
v = 16*v + 10 + (c-'a');
|
||||||
|
else if (c>='A' && c<='F')
|
||||||
|
v = 16*v + 10 + (c-'A');
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
s++;
|
||||||
|
c = *s;
|
||||||
|
l--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// decimal
|
||||||
|
|
||||||
|
while(l>0) {
|
||||||
|
if (c<'0' || c>'9') break;
|
||||||
|
v = 10*v + (c-'0');
|
||||||
|
s++;
|
||||||
|
c = *s;
|
||||||
|
l--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stripSpaces)
|
||||||
|
while(l>0) {
|
||||||
|
if (c != ' ') break;
|
||||||
|
s++;
|
||||||
|
c = *s;
|
||||||
|
l--;
|
||||||
|
}
|
||||||
|
|
||||||
|
_str = s;
|
||||||
|
_len = l;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FixString::stripSurroundingSpaces()
|
||||||
|
{
|
||||||
|
if (_len==0) return;
|
||||||
|
|
||||||
|
// leading spaces
|
||||||
|
while((_len>0) && (*_str==' ')) {
|
||||||
|
_len--;
|
||||||
|
_str++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// trailing spaces
|
||||||
|
while((_len>0) && (_str[_len-1]==' ')) {
|
||||||
|
_len--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixString::stripSpaces()
|
||||||
|
{
|
||||||
|
while((_len>0) && (*_str==' ')) {
|
||||||
|
_len--;
|
||||||
|
_str++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FixString::stripName(FixString& s)
|
||||||
|
{
|
||||||
|
if (_len==0) return false;
|
||||||
|
|
||||||
|
// first char has to be a letter or "_"
|
||||||
|
if (!QChar(*_str).isLetter() && (*_str != '_')) return false;
|
||||||
|
|
||||||
|
int newLen = 1;
|
||||||
|
const char* newStr = _str;
|
||||||
|
|
||||||
|
_str++;
|
||||||
|
_len--;
|
||||||
|
|
||||||
|
while(_len>0) {
|
||||||
|
if (!QChar(*_str).isLetterOrNumber()
|
||||||
|
&& (*_str != '_')) break;
|
||||||
|
|
||||||
|
newLen++;
|
||||||
|
_str++;
|
||||||
|
_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
s.set(newStr, newLen);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FixString FixString::stripUntil(char c)
|
||||||
|
{
|
||||||
|
if (_len == 0) return FixString();
|
||||||
|
|
||||||
|
const char* newStr = _str;
|
||||||
|
int newLen = 0;
|
||||||
|
|
||||||
|
while(_len>0) {
|
||||||
|
if (*_str == c) {
|
||||||
|
_str++;
|
||||||
|
_len--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_str++;
|
||||||
|
_len--;
|
||||||
|
newLen++;
|
||||||
|
}
|
||||||
|
return FixString(newStr, newLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FixString::stripUInt64(uint64& v, bool stripSpaces)
|
||||||
|
{
|
||||||
|
if (_len==0) {
|
||||||
|
v = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char c = *_str;
|
||||||
|
if (c<'0' || c>'9') {
|
||||||
|
v = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = c-'0';
|
||||||
|
const char* s = _str+1;
|
||||||
|
int l = _len-1;
|
||||||
|
c = *s;
|
||||||
|
|
||||||
|
if ((l>0) && (c == 'x') && (v==0)) {
|
||||||
|
// hexadecimal
|
||||||
|
s++;
|
||||||
|
c = *s;
|
||||||
|
l--;
|
||||||
|
|
||||||
|
while(l>0) {
|
||||||
|
if (c>='0' && c<='9')
|
||||||
|
v = 16*v + (c-'0');
|
||||||
|
else if (c>='a' && c<='f')
|
||||||
|
v = 16*v + 10 + (c-'a');
|
||||||
|
else if (c>='A' && c<='F')
|
||||||
|
v = 16*v + 10 + (c-'A');
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
s++;
|
||||||
|
c = *s;
|
||||||
|
l--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// decimal
|
||||||
|
while(l>0) {
|
||||||
|
if (c<'0' || c>'9') break;
|
||||||
|
v = 10*v + (c-'0');
|
||||||
|
s++;
|
||||||
|
c = *s;
|
||||||
|
l--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stripSpaces)
|
||||||
|
while(l>0) {
|
||||||
|
if (c != ' ') break;
|
||||||
|
s++;
|
||||||
|
c = *s;
|
||||||
|
l--;
|
||||||
|
}
|
||||||
|
|
||||||
|
_str = s;
|
||||||
|
_len = l;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool FixString::stripInt64(int64& v, bool stripSpaces)
|
||||||
|
{
|
||||||
|
if (_len==0) {
|
||||||
|
v = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char c = *_str;
|
||||||
|
if (c<'0' || c>'9') {
|
||||||
|
v = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = c-'0';
|
||||||
|
const char* s = _str+1;
|
||||||
|
int l = _len-1;
|
||||||
|
c = *s;
|
||||||
|
|
||||||
|
if ((l>0) && (c == 'x') && (v==0)) {
|
||||||
|
// hexadecimal
|
||||||
|
s++;
|
||||||
|
c = *s;
|
||||||
|
l--;
|
||||||
|
|
||||||
|
while(l>0) {
|
||||||
|
if (c>='0' && c<='9')
|
||||||
|
v = 16*v + (c-'0');
|
||||||
|
else if (c>='a' && c<='f')
|
||||||
|
v = 16*v + 10 + (c-'a');
|
||||||
|
else if (c>='A' && c<='F')
|
||||||
|
v = 16*v + 10 + (c-'A');
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
s++;
|
||||||
|
c = *s;
|
||||||
|
l--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// decimal
|
||||||
|
|
||||||
|
while(l>0) {
|
||||||
|
if (c<'0' || c>'9') break;
|
||||||
|
v = 10*v + (c-'0');
|
||||||
|
s++;
|
||||||
|
c = *s;
|
||||||
|
l--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stripSpaces)
|
||||||
|
while(l>0) {
|
||||||
|
if (c != ' ') break;
|
||||||
|
s++;
|
||||||
|
c = *s;
|
||||||
|
l--;
|
||||||
|
}
|
||||||
|
|
||||||
|
_str = s;
|
||||||
|
_len = l;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// class FixFile
|
||||||
|
|
||||||
|
FixFile::FixFile(QIODevice* file, const QString& filename)
|
||||||
|
{
|
||||||
|
_file = file;
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
_len = 0;
|
||||||
|
_currentLeft = 0;
|
||||||
|
_openError = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_filename = filename;
|
||||||
|
if (!file->isOpen() && !file->open( QIODevice::ReadOnly ) ) {
|
||||||
|
qWarning( "%s: %s", (const char*)QFile::encodeName(_filename),
|
||||||
|
strerror( errno ) );
|
||||||
|
_len = 0;
|
||||||
|
_currentLeft = 0;
|
||||||
|
_openError = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_openError = false;
|
||||||
|
_used_mmap = false;
|
||||||
|
|
||||||
|
uchar* addr = 0;
|
||||||
|
|
||||||
|
#if QT_VERSION >= 0x040400
|
||||||
|
// QFile::map was introduced with Qt 4.4
|
||||||
|
if (file->size() >0) {
|
||||||
|
QFile* mappableDevice = dynamic_cast<QFile*>(file);
|
||||||
|
if (mappableDevice) {
|
||||||
|
addr = mappableDevice->map( 0, file->size() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (addr) {
|
||||||
|
// map succeeded
|
||||||
|
_base = (char*) addr;
|
||||||
|
_len = file->size();
|
||||||
|
_used_mmap = true;
|
||||||
|
|
||||||
|
if (0) qDebug("Mapped '%s'", qPrintable( _filename ));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// try reading the data into memory instead
|
||||||
|
file->seek(0);
|
||||||
|
_data = file->readAll();
|
||||||
|
_base = _data.data();
|
||||||
|
_len = _data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
_current = _base;
|
||||||
|
_currentLeft = _len;
|
||||||
|
}
|
||||||
|
|
||||||
|
FixFile::~FixFile()
|
||||||
|
{
|
||||||
|
// if the file was read into _data, it will be deleted automatically
|
||||||
|
|
||||||
|
if (_used_mmap && _file) {
|
||||||
|
if (0) qDebug("Unmapping '%s'", qPrintable( _filename ));
|
||||||
|
#if QT_VERSION >= 0x040400
|
||||||
|
QFile* mappableDevice = dynamic_cast<QFile*>(_file);
|
||||||
|
Q_ASSERT(mappableDevice);
|
||||||
|
if (!mappableDevice->unmap( (uchar*) _base ))
|
||||||
|
qWarning( "munmap: %s", strerror( errno ) );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FixFile::nextLine(FixString& str)
|
||||||
|
{
|
||||||
|
if (_currentLeft == 0) return false;
|
||||||
|
|
||||||
|
unsigned left = _currentLeft;
|
||||||
|
char* current = _current;
|
||||||
|
|
||||||
|
while(left>0) {
|
||||||
|
if (*current == 0 || *current == '\n') break;
|
||||||
|
current++;
|
||||||
|
left--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0) {
|
||||||
|
char tmp[200];
|
||||||
|
int l = _currentLeft-left;
|
||||||
|
if (l>199) l = 199;
|
||||||
|
strncpy(tmp, _current, l);
|
||||||
|
tmp[l] = 0;
|
||||||
|
qDebug("[FixFile::nextLine] At %lu, len %u: '%s'",
|
||||||
|
(unsigned long) (_current - _base), _currentLeft-left, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = _currentLeft-left;
|
||||||
|
// get rid of any carriage return at end
|
||||||
|
if ((len>0) && (*(current-1) == '\r')) len--;
|
||||||
|
str.set(_current, len);
|
||||||
|
|
||||||
|
if (*current == '\n') {
|
||||||
|
current++;
|
||||||
|
left--;
|
||||||
|
}
|
||||||
|
_current = current;
|
||||||
|
_currentLeft = left;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FixFile::setCurrent(unsigned pos)
|
||||||
|
{
|
||||||
|
if (pos > _len) return false;
|
||||||
|
|
||||||
|
_current = _base + pos;
|
||||||
|
_currentLeft = _len - pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
// class AppendList
|
||||||
|
|
||||||
|
|
||||||
|
AppendList::AppendList()
|
||||||
|
{
|
||||||
|
_next = 0;
|
||||||
|
_current = 0;
|
||||||
|
_last = 0;
|
||||||
|
|
||||||
|
_count = 0;
|
||||||
|
_currentIndex = 0;
|
||||||
|
_lastIndex = 0;
|
||||||
|
_autoDelete = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AppendList::clear()
|
||||||
|
{
|
||||||
|
int count = _count;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (count <= firstLen) {
|
||||||
|
if (_autoDelete)
|
||||||
|
for (i=0;i<count;i++)
|
||||||
|
delete _first[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
165
kcachegrind/libcore/utils.h
Normal file
165
kcachegrind/libcore/utils.h
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility classes for KCachegrind
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include <qstring.h>
|
||||||
|
|
||||||
|
class QIODevice;
|
||||||
|
|
||||||
|
typedef unsigned long long uint64;
|
||||||
|
typedef long long int64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple, constant string class
|
||||||
|
*
|
||||||
|
* For use with zero-copy strings from mapped files.
|
||||||
|
*/
|
||||||
|
class FixString {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// constructor for an invalid string
|
||||||
|
FixString() { _len = 0; _str = 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FixString never does a deep copy! You have to make sure that
|
||||||
|
* the string starting at the char pointer is valid trough the
|
||||||
|
* lifetime of FixString.
|
||||||
|
*/
|
||||||
|
FixString(const char*, int len);
|
||||||
|
|
||||||
|
int len() { return _len; }
|
||||||
|
const char* ascii() { return _str; }
|
||||||
|
bool isEmpty() { return _len == 0; }
|
||||||
|
bool isValid() { return _str != 0; }
|
||||||
|
|
||||||
|
// sets <c> to first character and returns true if length >0
|
||||||
|
bool first(char& c)
|
||||||
|
{ if (_len==0) return false; c=_str[0]; return true; }
|
||||||
|
|
||||||
|
void set(const char* s, int l) { _str=s; _len=l; }
|
||||||
|
bool stripFirst(char&);
|
||||||
|
bool stripPrefix(const char*);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip leading and trailing spaces
|
||||||
|
*/
|
||||||
|
void stripSurroundingSpaces();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip leading spaces
|
||||||
|
*/
|
||||||
|
void stripSpaces();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip name: [A-Za-z_][0-9A_Za-z_]*
|
||||||
|
*/
|
||||||
|
bool stripName(FixString&);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip string until char appears or end. Strips char, too.
|
||||||
|
*/
|
||||||
|
FixString stripUntil(char);
|
||||||
|
|
||||||
|
bool stripUInt(uint&, bool stripSpaces = true);
|
||||||
|
bool stripUInt64(uint64&, bool stripSpaces = true);
|
||||||
|
bool stripInt64(int64&, bool stripSpaces = true);
|
||||||
|
|
||||||
|
operator QString() const
|
||||||
|
{ return QString::fromLocal8Bit(_str,_len); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char* _str;
|
||||||
|
int _len;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for fast line by line reading of a read-only ASCII file
|
||||||
|
*/
|
||||||
|
class FixFile {
|
||||||
|
|
||||||
|
public:
|
||||||
|
FixFile(QIODevice*, const QString&);
|
||||||
|
~FixFile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read next line into <str>. Returns false on error or EOF.
|
||||||
|
*/
|
||||||
|
bool nextLine(FixString& str);
|
||||||
|
bool exists() { return !_openError; }
|
||||||
|
unsigned len() { return _len; }
|
||||||
|
unsigned current() { return _current - _base; }
|
||||||
|
bool setCurrent(unsigned pos);
|
||||||
|
void rewind() { setCurrent(0); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
char *_base, *_current;
|
||||||
|
QByteArray _data;
|
||||||
|
unsigned _len, _currentLeft;
|
||||||
|
bool _used_mmap, _openError;
|
||||||
|
QIODevice* _file;
|
||||||
|
QString _filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of pointers, only able to append items.
|
||||||
|
* Optimized for speed, not space.
|
||||||
|
*/
|
||||||
|
template<class type>
|
||||||
|
class AppendList {
|
||||||
|
|
||||||
|
public:
|
||||||
|
AppendList();
|
||||||
|
~AppendList() { clear(); }
|
||||||
|
|
||||||
|
void setAutoDelete(bool);
|
||||||
|
void clear();
|
||||||
|
void append(const type*);
|
||||||
|
|
||||||
|
unsigned count() const { return _count; }
|
||||||
|
unsigned containsRef(const type*) const;
|
||||||
|
|
||||||
|
type* current();
|
||||||
|
type* first();
|
||||||
|
type* next();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const int firstLen = 8;
|
||||||
|
static const int maxLen = 256;
|
||||||
|
|
||||||
|
struct AppendListChunk {
|
||||||
|
int size;
|
||||||
|
struct AppendListChunk* next;
|
||||||
|
type* data[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AppendListChunk *_next, *_current, *_last;
|
||||||
|
int _count, _currentIndex, _lastIndex;
|
||||||
|
bool _autoDelete;
|
||||||
|
type* _first[firstLen];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
35
kcachegrind/libviews/CMakeLists.txt
Normal file
35
kcachegrind/libviews/CMakeLists.txt
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
include_directories( ../libcore )
|
||||||
|
|
||||||
|
set(libviews_SRCS
|
||||||
|
globalguiconfig.cpp
|
||||||
|
stackitem.cpp
|
||||||
|
stackselection.cpp
|
||||||
|
partgraph.cpp
|
||||||
|
partselection.cpp
|
||||||
|
costlistitem.cpp
|
||||||
|
functionlistmodel.cpp
|
||||||
|
functionselection.cpp
|
||||||
|
toplevelbase.cpp
|
||||||
|
listutils.cpp
|
||||||
|
treemap.cpp
|
||||||
|
traceitemview.cpp
|
||||||
|
tabview.cpp
|
||||||
|
multiview.cpp
|
||||||
|
instrview.cpp
|
||||||
|
sourceview.cpp
|
||||||
|
callmapview.cpp
|
||||||
|
callgraphview.cpp
|
||||||
|
callview.cpp
|
||||||
|
coverageview.cpp
|
||||||
|
eventtypeview.cpp
|
||||||
|
partview.cpp
|
||||||
|
eventtypeitem.cpp
|
||||||
|
callitem.cpp
|
||||||
|
coverageitem.cpp
|
||||||
|
sourceitem.cpp
|
||||||
|
instritem.cpp
|
||||||
|
partlistitem.cpp )
|
||||||
|
|
||||||
|
qt4_automoc(${libviews_SRCS})
|
||||||
|
add_library(views STATIC ${libviews_SRCS})
|
||||||
|
target_link_libraries(views core)
|
87
kcachegrind/libviews/README
Normal file
87
kcachegrind/libviews/README
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
OVERVIEW
|
||||||
|
|
||||||
|
This directory contains widgets which display views
|
||||||
|
into profile data. A view either shows some aspect of
|
||||||
|
the profile data directly, are is a container for
|
||||||
|
other views.
|
||||||
|
|
||||||
|
All views are subclasses of TraceItemView.
|
||||||
|
|
||||||
|
Implementation of all views depend on Qt only.
|
||||||
|
KDE-specific views, these should be in kcachegrind/.
|
||||||
|
|
||||||
|
|
||||||
|
VIEWS
|
||||||
|
|
||||||
|
|
||||||
|
Container views
|
||||||
|
---------------
|
||||||
|
|
||||||
|
TabView
|
||||||
|
|
||||||
|
A Tabview embeds multiple views with the same cost item
|
||||||
|
selected/activated. The views can be arranged either
|
||||||
|
on top of each other using tabs for selection, or nearside
|
||||||
|
each other in four regions (right/top/left/bottom).
|
||||||
|
|
||||||
|
Multiview
|
||||||
|
|
||||||
|
A Multiview is a horizontal or vertical series of embedded views,
|
||||||
|
separated by splitters.
|
||||||
|
Eeach embedded view can have its own cost item selection/activation.
|
||||||
|
One of the views has the focus, and thus providing the
|
||||||
|
selection/activation of the multiview itself.
|
||||||
|
Selection in one view changes the activation in the next view to
|
||||||
|
the right/bottom (with wrap around).
|
||||||
|
|
||||||
|
|
||||||
|
Detailed views
|
||||||
|
--------------
|
||||||
|
|
||||||
|
EventTypeView
|
||||||
|
|
||||||
|
A list of event types measured in the profile experiment,
|
||||||
|
attributed with the costs of the currently activated cost item.
|
||||||
|
|
||||||
|
CallView
|
||||||
|
|
||||||
|
A list of callers/callees of the currently activated cost item.
|
||||||
|
|
||||||
|
CallMapView
|
||||||
|
|
||||||
|
A treemap showing the nesting of callers/callees starting from
|
||||||
|
the currently activated cost item.
|
||||||
|
|
||||||
|
CallGraphView
|
||||||
|
|
||||||
|
A graph around the currently activated cost item, with edges
|
||||||
|
being the call relations. Only nodes and edges with cost over a
|
||||||
|
given threshould are shown.
|
||||||
|
|
||||||
|
CoverageView
|
||||||
|
|
||||||
|
Similar to the CallView list, the coverage list includes not
|
||||||
|
only direct callers/callees, but also indirect.
|
||||||
|
|
||||||
|
SourceView
|
||||||
|
|
||||||
|
Annotated source.
|
||||||
|
|
||||||
|
InstrView
|
||||||
|
|
||||||
|
Annotated assembly.
|
||||||
|
|
||||||
|
PartView
|
||||||
|
|
||||||
|
List of loaded profile data parts
|
||||||
|
|
||||||
|
|
||||||
|
Misc
|
||||||
|
----
|
||||||
|
|
||||||
|
TreeMap
|
||||||
|
|
||||||
|
Generic widget for treemaps
|
||||||
|
|
||||||
|
listutils.cpp
|
||||||
|
helpers for lists in views
|
3185
kcachegrind/libviews/callgraphview.cpp
Normal file
3185
kcachegrind/libviews/callgraphview.cpp
Normal file
File diff suppressed because it is too large
Load diff
686
kcachegrind/libviews/callgraphview.h
Normal file
686
kcachegrind/libviews/callgraphview.h
Normal file
|
@ -0,0 +1,686 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callgraph View
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CALLGRAPHVIEW_H
|
||||||
|
#define CALLGRAPHVIEW_H
|
||||||
|
|
||||||
|
#include <qwidget.h>
|
||||||
|
#include <qmap.h>
|
||||||
|
#include <qtimer.h>
|
||||||
|
|
||||||
|
#include <QGraphicsView>
|
||||||
|
#include <QGraphicsScene>
|
||||||
|
#include <QGraphicsRectItem>
|
||||||
|
#include <QGraphicsPolygonItem>
|
||||||
|
#include <QGraphicsPathItem>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QFocusEvent>
|
||||||
|
#include <QPolygon>
|
||||||
|
#include <QList>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
#include <QContextMenuEvent>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
#include "treemap.h" // for DrawParams
|
||||||
|
#include "tracedata.h"
|
||||||
|
#include "traceitemview.h"
|
||||||
|
|
||||||
|
class QProcess;
|
||||||
|
class QTemporaryFile;
|
||||||
|
class QIODevice;
|
||||||
|
|
||||||
|
class CanvasNode;
|
||||||
|
class CanvasEdge;
|
||||||
|
class GraphEdge;
|
||||||
|
class CallGraphView;
|
||||||
|
|
||||||
|
|
||||||
|
// temporary parts of call graph to be shown
|
||||||
|
class GraphNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GraphNode();
|
||||||
|
|
||||||
|
TraceFunction* function() const
|
||||||
|
{
|
||||||
|
return _f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFunction(TraceFunction* f)
|
||||||
|
{
|
||||||
|
_f = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasNode* canvasNode() const
|
||||||
|
{
|
||||||
|
return _cn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCanvasNode(CanvasNode* cn)
|
||||||
|
{
|
||||||
|
_cn = cn;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isVisible() const
|
||||||
|
{
|
||||||
|
return _visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVisible(bool v)
|
||||||
|
{
|
||||||
|
_visible = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearEdges();
|
||||||
|
void sortEdges();
|
||||||
|
void addCallee(GraphEdge*);
|
||||||
|
void addCaller(GraphEdge*);
|
||||||
|
void addUniqueCallee(GraphEdge*);
|
||||||
|
void addUniqueCaller(GraphEdge*);
|
||||||
|
void removeEdge(GraphEdge*);
|
||||||
|
double calleeCostSum();
|
||||||
|
double calleeCountSum();
|
||||||
|
double callerCostSum();
|
||||||
|
double callerCountSum();
|
||||||
|
|
||||||
|
// keyboard navigation
|
||||||
|
TraceCall* visibleCaller();
|
||||||
|
TraceCall* visibleCallee();
|
||||||
|
void setCallee(GraphEdge*);
|
||||||
|
void setCaller(GraphEdge*);
|
||||||
|
TraceFunction* nextVisible();
|
||||||
|
TraceFunction* priorVisible();
|
||||||
|
TraceCall* nextVisibleCaller(GraphEdge* = 0);
|
||||||
|
TraceCall* nextVisibleCallee(GraphEdge* = 0);
|
||||||
|
TraceCall* priorVisibleCaller(GraphEdge* = 0);
|
||||||
|
TraceCall* priorVisibleCallee(GraphEdge* = 0);
|
||||||
|
|
||||||
|
double self, incl;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TraceFunction* _f;
|
||||||
|
CanvasNode* _cn;
|
||||||
|
bool _visible;
|
||||||
|
|
||||||
|
QList<GraphEdge*> callers, callees;
|
||||||
|
|
||||||
|
// for keyboard navigation
|
||||||
|
int _lastCallerIndex, _lastCalleeIndex;
|
||||||
|
bool _lastFromCaller;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class GraphEdge
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GraphEdge();
|
||||||
|
|
||||||
|
CanvasEdge* canvasEdge() const
|
||||||
|
{
|
||||||
|
return _ce;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCanvasEdge(CanvasEdge* ce)
|
||||||
|
{
|
||||||
|
_ce = ce;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceCall* call() const
|
||||||
|
{
|
||||||
|
return _c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCall(TraceCall* c)
|
||||||
|
{
|
||||||
|
_c = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isVisible() const
|
||||||
|
{
|
||||||
|
return _visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVisible(bool v)
|
||||||
|
{
|
||||||
|
_visible = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphNode* fromNode() const
|
||||||
|
{
|
||||||
|
return _fromNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphNode* toNode() const
|
||||||
|
{
|
||||||
|
return _toNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceFunction* from() const
|
||||||
|
{
|
||||||
|
return _from;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceFunction* to() const
|
||||||
|
{
|
||||||
|
return _to;
|
||||||
|
}
|
||||||
|
|
||||||
|
// has special cases for collapsed edges
|
||||||
|
QString prettyName();
|
||||||
|
|
||||||
|
void setCaller(TraceFunction* f)
|
||||||
|
{
|
||||||
|
_from = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCallee(TraceFunction* f)
|
||||||
|
{
|
||||||
|
_to = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCallerNode(GraphNode* n)
|
||||||
|
{
|
||||||
|
_fromNode = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCalleeNode(GraphNode* n)
|
||||||
|
{
|
||||||
|
_toNode = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyboard navigation
|
||||||
|
TraceFunction* visibleCaller();
|
||||||
|
TraceFunction* visibleCallee();
|
||||||
|
TraceCall* nextVisible();
|
||||||
|
TraceCall* priorVisible();
|
||||||
|
|
||||||
|
double cost, count;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// we have a _c *and* _from/_to because for collapsed edges,
|
||||||
|
// only _to or _from will be unequal NULL
|
||||||
|
TraceCall* _c;
|
||||||
|
TraceFunction * _from, * _to;
|
||||||
|
GraphNode *_fromNode, *_toNode;
|
||||||
|
CanvasEdge* _ce;
|
||||||
|
bool _visible;
|
||||||
|
// for keyboard navigation: have we last reached this edge via a caller?
|
||||||
|
bool _lastFromCaller;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef QMap<TraceFunction*, GraphNode> GraphNodeMap;
|
||||||
|
typedef QMap<QPair<TraceFunction*, TraceFunction*>, GraphEdge> GraphEdgeMap;
|
||||||
|
|
||||||
|
|
||||||
|
/* Abstract Interface for graph options */
|
||||||
|
class GraphOptions
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Layout {TopDown, LeftRight, Circular};
|
||||||
|
virtual ~GraphOptions() {}
|
||||||
|
virtual double funcLimit() = 0;
|
||||||
|
virtual double callLimit() = 0;
|
||||||
|
virtual int maxCallerDepth() = 0;
|
||||||
|
virtual int maxCalleeDepth() = 0;
|
||||||
|
virtual bool showSkipped() = 0;
|
||||||
|
virtual bool expandCycles() = 0;
|
||||||
|
virtual bool clusterGroups() = 0;
|
||||||
|
virtual int detailLevel() = 0;
|
||||||
|
virtual Layout layout() = 0;
|
||||||
|
|
||||||
|
static QString layoutString(Layout);
|
||||||
|
static Layout layout(QString);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Graph Options Storage */
|
||||||
|
class StorableGraphOptions: public GraphOptions
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StorableGraphOptions();
|
||||||
|
virtual ~StorableGraphOptions(){}
|
||||||
|
// implementation of getters
|
||||||
|
virtual double funcLimit() { return _funcLimit; }
|
||||||
|
virtual double callLimit() { return _callLimit; }
|
||||||
|
virtual int maxCallerDepth() { return _maxCallerDepth; }
|
||||||
|
virtual int maxCalleeDepth() { return _maxCalleeDepth; }
|
||||||
|
virtual bool showSkipped() { return _showSkipped; }
|
||||||
|
virtual bool expandCycles() { return _expandCycles; }
|
||||||
|
virtual bool clusterGroups() { return _clusterGroups; }
|
||||||
|
virtual int detailLevel() { return _detailLevel; }
|
||||||
|
virtual Layout layout() { return _layout; }
|
||||||
|
|
||||||
|
// setters
|
||||||
|
void setMaxCallerDepth(int d) { _maxCallerDepth = d; }
|
||||||
|
void setMaxCalleeDepth(int d) { _maxCalleeDepth = d; }
|
||||||
|
void setFuncLimit(double l) { _funcLimit = l; }
|
||||||
|
void setCallLimit(double l) { _callLimit = l; }
|
||||||
|
void setShowSkipped(bool b) { _showSkipped = b; }
|
||||||
|
void setExpandCycles(bool b) { _expandCycles = b; }
|
||||||
|
void setClusterGroups(bool b) { _clusterGroups = b; }
|
||||||
|
void setDetailLevel(int l) { _detailLevel = l; }
|
||||||
|
void setLayout(Layout l) { _layout = l; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
double _funcLimit, _callLimit;
|
||||||
|
int _maxCallerDepth, _maxCalleeDepth;
|
||||||
|
bool _showSkipped, _expandCycles, _clusterGroups;
|
||||||
|
int _detailLevel;
|
||||||
|
Layout _layout;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GraphExporter
|
||||||
|
*
|
||||||
|
* Generates a graph file for "dot"
|
||||||
|
* Create an instance and
|
||||||
|
*/
|
||||||
|
class GraphExporter : public StorableGraphOptions
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GraphExporter();
|
||||||
|
GraphExporter(TraceData*, TraceFunction*, EventType*,
|
||||||
|
ProfileContext::Type,
|
||||||
|
QString filename = QString());
|
||||||
|
virtual ~GraphExporter();
|
||||||
|
|
||||||
|
void reset(TraceData*, CostItem*, EventType*,
|
||||||
|
ProfileContext::Type,
|
||||||
|
QString filename = QString());
|
||||||
|
|
||||||
|
QString filename()
|
||||||
|
{
|
||||||
|
return _dotName;
|
||||||
|
}
|
||||||
|
|
||||||
|
int edgeCount()
|
||||||
|
{
|
||||||
|
return _edgeMap.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
int nodeCount()
|
||||||
|
{
|
||||||
|
return _nodeMap.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the object from which to get graph options for creation.
|
||||||
|
// Default is this object itself (supply 0 for default)
|
||||||
|
void setGraphOptions(GraphOptions* go = 0);
|
||||||
|
|
||||||
|
// Create a subgraph with given limits/maxDepths
|
||||||
|
void createGraph();
|
||||||
|
|
||||||
|
// calls createGraph before dumping of not already created
|
||||||
|
void writeDot(QIODevice* = 0);
|
||||||
|
|
||||||
|
// to map back to structures when parsing a layouted graph
|
||||||
|
|
||||||
|
/* <toFunc> is a helper for node() and edge().
|
||||||
|
* Do not use the returned pointer directly, but only with
|
||||||
|
* node() or edge(), because it could be a dangling pointer.
|
||||||
|
*/
|
||||||
|
TraceFunction* toFunc(QString);
|
||||||
|
GraphNode* node(TraceFunction*);
|
||||||
|
GraphEdge* edge(TraceFunction*, TraceFunction*);
|
||||||
|
|
||||||
|
/* After CanvasEdges are attached to GraphEdges, we can
|
||||||
|
* sort the incoming and outgoing edges of all nodes
|
||||||
|
* regarding start/end points for keyboard navigation
|
||||||
|
*/
|
||||||
|
void sortEdges();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void buildGraph(TraceFunction*, int, bool, double);
|
||||||
|
|
||||||
|
QString _dotName;
|
||||||
|
CostItem* _item;
|
||||||
|
EventType* _eventType;
|
||||||
|
ProfileContext::Type _groupType;
|
||||||
|
QTemporaryFile* _tmpFile;
|
||||||
|
double _realFuncLimit, _realCallLimit;
|
||||||
|
int _maxDepth;
|
||||||
|
bool _graphCreated;
|
||||||
|
|
||||||
|
GraphOptions* _go;
|
||||||
|
|
||||||
|
// optional graph attributes
|
||||||
|
bool _useBox;
|
||||||
|
|
||||||
|
// graph parts written to file
|
||||||
|
GraphNodeMap _nodeMap;
|
||||||
|
GraphEdgeMap _edgeMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A panner laid over a QGraphicsScene
|
||||||
|
*/
|
||||||
|
class PanningView : public QGraphicsView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PanningView(QWidget * parent = 0);
|
||||||
|
|
||||||
|
void setZoomRect(const QRectF& r);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void zoomRectMoved(qreal dx, qreal dy);
|
||||||
|
void zoomRectMoveFinished();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mousePressEvent(QMouseEvent*);
|
||||||
|
void mouseMoveEvent(QMouseEvent*);
|
||||||
|
void mouseReleaseEvent(QMouseEvent*);
|
||||||
|
void drawForeground(QPainter * p, const QRectF&);
|
||||||
|
|
||||||
|
QRectF _zoomRect;
|
||||||
|
bool _movingZoomRect;
|
||||||
|
QPointF _lastPos;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Canvas Items:
|
||||||
|
* - CanvasNode (Rectangular Area)
|
||||||
|
* - CanvasEdge (Spline curve)
|
||||||
|
* - CanvasEdgeLabel (Label for edges)
|
||||||
|
* - CanvasEdgeArrow (Arrows at the end of the edge spline)
|
||||||
|
* - CanvasFrame (Grey background blending to show active node)
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CANVAS_NODE = 1122,
|
||||||
|
CANVAS_EDGE, CANVAS_EDGELABEL, CANVAS_EDGEARROW,
|
||||||
|
CANVAS_FRAME
|
||||||
|
};
|
||||||
|
|
||||||
|
class CanvasNode : public QGraphicsRectItem, public StoredDrawParams
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanvasNode(CallGraphView*, GraphNode*, int, int, int, int);
|
||||||
|
|
||||||
|
void updateGroup();
|
||||||
|
void setSelected(bool);
|
||||||
|
void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*);
|
||||||
|
|
||||||
|
GraphNode* node()
|
||||||
|
{
|
||||||
|
return _node;
|
||||||
|
}
|
||||||
|
|
||||||
|
int type() const
|
||||||
|
{
|
||||||
|
return CANVAS_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GraphNode* _node;
|
||||||
|
CallGraphView* _view;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CanvasEdgeLabel : public QGraphicsRectItem, public StoredDrawParams
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanvasEdgeLabel(CallGraphView*, CanvasEdge*, int, int, int, int);
|
||||||
|
|
||||||
|
void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*);
|
||||||
|
|
||||||
|
CanvasEdge* canvasEdge()
|
||||||
|
{
|
||||||
|
return _ce;
|
||||||
|
}
|
||||||
|
|
||||||
|
int type() const
|
||||||
|
{
|
||||||
|
return CANVAS_EDGELABEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
double percentage() const
|
||||||
|
{
|
||||||
|
return _percentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CanvasEdge* _ce;
|
||||||
|
CallGraphView* _view;
|
||||||
|
|
||||||
|
double _percentage;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CanvasEdgeArrow : public QGraphicsPolygonItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanvasEdgeArrow(CanvasEdge*);
|
||||||
|
|
||||||
|
void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*);
|
||||||
|
|
||||||
|
CanvasEdge* canvasEdge()
|
||||||
|
{
|
||||||
|
return _ce;
|
||||||
|
}
|
||||||
|
|
||||||
|
int type() const
|
||||||
|
{
|
||||||
|
return CANVAS_EDGEARROW;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CanvasEdge* _ce;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CanvasEdge : public QGraphicsPathItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanvasEdge(GraphEdge*);
|
||||||
|
|
||||||
|
void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*);
|
||||||
|
|
||||||
|
void setSelected(bool);
|
||||||
|
|
||||||
|
CanvasEdgeLabel* label()
|
||||||
|
{
|
||||||
|
return _label;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLabel(CanvasEdgeLabel* l);
|
||||||
|
|
||||||
|
CanvasEdgeArrow* arrow()
|
||||||
|
{
|
||||||
|
return _arrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setArrow(CanvasEdgeArrow* a);
|
||||||
|
|
||||||
|
const QPolygon& controlPoints() const
|
||||||
|
{
|
||||||
|
return _points;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setControlPoints(const QPolygon& a);
|
||||||
|
|
||||||
|
GraphEdge* edge()
|
||||||
|
{
|
||||||
|
return _edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
int type() const
|
||||||
|
{
|
||||||
|
return CANVAS_EDGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GraphEdge* _edge;
|
||||||
|
CanvasEdgeLabel* _label;
|
||||||
|
CanvasEdgeArrow* _arrow;
|
||||||
|
QPolygon _points;
|
||||||
|
|
||||||
|
double _thickness;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CanvasFrame : public QGraphicsRectItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanvasFrame(CanvasNode*);
|
||||||
|
|
||||||
|
int type() const
|
||||||
|
{
|
||||||
|
return CANVAS_FRAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hit(const QPoint&) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QPixmap* _p;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CallGraphTip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A QGraphicsView showing a part of the call graph
|
||||||
|
* and another zoomed out CanvasView in a border acting as
|
||||||
|
* a panner to select to visible part (only if needed)
|
||||||
|
*/
|
||||||
|
class CallGraphView : public QGraphicsView, public TraceItemView,
|
||||||
|
public StorableGraphOptions
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum ZoomPosition {TopLeft, TopRight, BottomLeft, BottomRight, Auto, Hide};
|
||||||
|
|
||||||
|
explicit CallGraphView(TraceItemView* parentView, QWidget* parent=0,
|
||||||
|
const char* name = 0);
|
||||||
|
~CallGraphView();
|
||||||
|
|
||||||
|
void restoreOptions(const QString& prefix, const QString& postfix);
|
||||||
|
void saveOptions(const QString& prefix, const QString& postfix);
|
||||||
|
|
||||||
|
QWidget* widget()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString whatsThis() const;
|
||||||
|
|
||||||
|
ZoomPosition zoomPos() const
|
||||||
|
{
|
||||||
|
return _zoomPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ZoomPosition zoomPos(QString);
|
||||||
|
static QString zoomPosString(ZoomPosition);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void zoomRectMoved(qreal, qreal);
|
||||||
|
void zoomRectMoveFinished();
|
||||||
|
|
||||||
|
void showRenderWarning();
|
||||||
|
void showRenderError(QString);
|
||||||
|
void stopRendering();
|
||||||
|
void readDotOutput();
|
||||||
|
void dotError();
|
||||||
|
void dotExited();
|
||||||
|
|
||||||
|
// context menu trigger handlers
|
||||||
|
void callerDepthTriggered(QAction*);
|
||||||
|
void calleeDepthTriggered(QAction*);
|
||||||
|
void nodeLimitTriggered(QAction*);
|
||||||
|
void callLimitTriggered(QAction*);
|
||||||
|
void zoomPosTriggered(QAction*);
|
||||||
|
void layoutTriggered(QAction*);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent*);
|
||||||
|
void mousePressEvent(QMouseEvent*);
|
||||||
|
void mouseMoveEvent(QMouseEvent*);
|
||||||
|
void mouseReleaseEvent(QMouseEvent*);
|
||||||
|
void mouseDoubleClickEvent(QMouseEvent*);
|
||||||
|
void contextMenuEvent(QContextMenuEvent*);
|
||||||
|
void keyPressEvent(QKeyEvent*);
|
||||||
|
void focusInEvent(QFocusEvent*);
|
||||||
|
void focusOutEvent(QFocusEvent*);
|
||||||
|
void scrollContentsBy(int dx, int dy);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateSizes(QSize s = QSize(0,0));
|
||||||
|
CostItem* canShow(CostItem*);
|
||||||
|
void doUpdate(int, bool);
|
||||||
|
void refresh();
|
||||||
|
void makeFrame(CanvasNode*, bool active);
|
||||||
|
void clear();
|
||||||
|
void showText(QString);
|
||||||
|
|
||||||
|
// context menu builders
|
||||||
|
QAction* addCallerDepthAction(QMenu*,QString,int);
|
||||||
|
QMenu* addCallerDepthMenu(QMenu*);
|
||||||
|
QAction* addCalleeDepthAction(QMenu*,QString,int);
|
||||||
|
QMenu* addCalleeDepthMenu(QMenu*);
|
||||||
|
QAction* addNodeLimitAction(QMenu*,QString,double);
|
||||||
|
QMenu* addNodeLimitMenu(QMenu*);
|
||||||
|
QAction* addCallLimitAction(QMenu*,QString,double);
|
||||||
|
QMenu* addCallLimitMenu(QMenu*);
|
||||||
|
QAction* addZoomPosAction(QMenu*,QString,ZoomPosition);
|
||||||
|
QMenu* addZoomPosMenu(QMenu*);
|
||||||
|
QAction* addLayoutAction(QMenu*,QString,Layout);
|
||||||
|
QMenu* addLayoutMenu(QMenu*);
|
||||||
|
|
||||||
|
QGraphicsScene *_scene;
|
||||||
|
int _xMargin, _yMargin;
|
||||||
|
PanningView *_panningView;
|
||||||
|
double _panningZoom;
|
||||||
|
|
||||||
|
CallGraphTip* _tip;
|
||||||
|
|
||||||
|
bool _isMoving;
|
||||||
|
QPoint _lastPos;
|
||||||
|
|
||||||
|
GraphExporter _exporter;
|
||||||
|
|
||||||
|
GraphNode* _selectedNode;
|
||||||
|
GraphEdge* _selectedEdge;
|
||||||
|
|
||||||
|
// widget options
|
||||||
|
ZoomPosition _zoomPosition, _lastAutoPosition;
|
||||||
|
|
||||||
|
// background rendering
|
||||||
|
QProcess* _renderProcess;
|
||||||
|
QString _renderProcessCmdLine;
|
||||||
|
QTimer _renderTimer;
|
||||||
|
GraphNode* _prevSelectedNode;
|
||||||
|
QPoint _prevSelectedPos;
|
||||||
|
QString _unparsedOutput;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
195
kcachegrind/libviews/callitem.cpp
Normal file
195
kcachegrind/libviews/callitem.cpp
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002, 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Items for caller/callee view.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "callitem.h"
|
||||||
|
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
#include "globalguiconfig.h"
|
||||||
|
#include "listutils.h"
|
||||||
|
#include "callview.h"
|
||||||
|
|
||||||
|
|
||||||
|
// CallItem
|
||||||
|
|
||||||
|
|
||||||
|
CallItem::CallItem(CallView* view, QTreeWidget* parent, TraceCall* c)
|
||||||
|
: QTreeWidgetItem(parent)
|
||||||
|
{
|
||||||
|
for (int i = 0 ; i < 5; ++i)
|
||||||
|
setTextAlignment(i, Qt::AlignRight);
|
||||||
|
|
||||||
|
_call = c;
|
||||||
|
_view = view;
|
||||||
|
|
||||||
|
_active = _view->activeFunction();
|
||||||
|
bool baseIsCycle = (_active && (_active == _active->cycle()));
|
||||||
|
|
||||||
|
QString fName;
|
||||||
|
if (_view->showCallers()) {
|
||||||
|
_shown = _call->caller(true);
|
||||||
|
fName = c->callerName(!baseIsCycle);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_shown = _call->called(true);
|
||||||
|
fName = c->calledName(!baseIsCycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
_shown->addPrettyLocation(fName);
|
||||||
|
|
||||||
|
setText(5, fName);
|
||||||
|
updateGroup();
|
||||||
|
updateCost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallItem::updateGroup()
|
||||||
|
{
|
||||||
|
QColor c = GlobalGUIConfig::functionColor(_view->groupType(), _shown);
|
||||||
|
setIcon(5, colorPixmap(10, 10, c));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallItem::updateCost()
|
||||||
|
{
|
||||||
|
bool sameCycle = _shown->cycle() && (_active->cycle() == _shown->cycle());
|
||||||
|
bool shownIsCycle = (_shown == _shown->cycle());
|
||||||
|
bool selectedIsCycle = (_active == _active->cycle());
|
||||||
|
if (_call->isRecursion()) sameCycle=true;
|
||||||
|
|
||||||
|
QString cStr;
|
||||||
|
if ((selectedIsCycle || shownIsCycle) && sameCycle)
|
||||||
|
cStr = "-";
|
||||||
|
else {
|
||||||
|
_cc = _call->callCount();
|
||||||
|
if (_cc == 0)
|
||||||
|
cStr = QObject::tr("(active)");
|
||||||
|
else
|
||||||
|
cStr = _call->prettyCallCount();
|
||||||
|
}
|
||||||
|
setText(4, cStr);
|
||||||
|
|
||||||
|
ProfileCostArray* totalCost;
|
||||||
|
if (GlobalConfig::showExpanded()) {
|
||||||
|
if (_active->cycle())
|
||||||
|
totalCost = _active->cycle()->inclusive();
|
||||||
|
else
|
||||||
|
totalCost = _active->inclusive();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
totalCost = _active->data();
|
||||||
|
|
||||||
|
EventType* ct = _view->eventType();
|
||||||
|
_sum = _call->subCost(ct);
|
||||||
|
double total = totalCost->subCost(ct);
|
||||||
|
|
||||||
|
if (total == 0.0) {
|
||||||
|
QString str = "-";
|
||||||
|
|
||||||
|
setText(0, str);
|
||||||
|
setIcon(0, QPixmap());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
double sum = 100.0 * _sum / total;
|
||||||
|
|
||||||
|
if (GlobalConfig::showPercentage())
|
||||||
|
setText(0, QString("%1")
|
||||||
|
.arg(sum, 0, 'f', GlobalConfig::percentPrecision()));
|
||||||
|
else {
|
||||||
|
setText(0, _call->prettySubCost(ct));
|
||||||
|
}
|
||||||
|
setIcon(0, costPixmap(ct, _call, total, false));
|
||||||
|
setText(1, _call->prettySubCostPerCall(ct, _cc));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cost Type 2
|
||||||
|
EventType* ct2 = _view->eventType2();
|
||||||
|
if (ct2) {
|
||||||
|
_sum2 = _call->subCost(ct2);
|
||||||
|
double total = totalCost->subCost(ct2);
|
||||||
|
|
||||||
|
if (total == 0.0) {
|
||||||
|
QString str = "-";
|
||||||
|
|
||||||
|
setText(2, str);
|
||||||
|
setIcon(2, QPixmap());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
double sum = 100.0 * _sum2 / total;
|
||||||
|
|
||||||
|
if (GlobalConfig::showPercentage())
|
||||||
|
setText(2, QString("%1")
|
||||||
|
.arg(sum, 0, 'f', GlobalConfig::percentPrecision()));
|
||||||
|
else {
|
||||||
|
setText(2, _call->prettySubCost(ct2));
|
||||||
|
}
|
||||||
|
setIcon(2, costPixmap(ct2, _call, total, false));
|
||||||
|
setText(3, _call->prettySubCostPerCall(ct2, _cc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap p;
|
||||||
|
if (sameCycle && !selectedIsCycle && !shownIsCycle) {
|
||||||
|
|
||||||
|
QString icon = "edit-undo";
|
||||||
|
#if 0 // TODO
|
||||||
|
KIconLoader* loader = KIconLoader::global();
|
||||||
|
p= loader->loadIcon(icon, KIconLoader::Small, 0,
|
||||||
|
KIconLoader::DefaultState, QStringList(), 0, true);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
setIcon(4, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CallItem::operator<(const QTreeWidgetItem& other) const
|
||||||
|
{
|
||||||
|
int col = treeWidget()->sortColumn();
|
||||||
|
const CallItem* ci1 = this;
|
||||||
|
const CallItem* ci2 = (CallItem*) &other;
|
||||||
|
|
||||||
|
if (col==0)
|
||||||
|
return ci1->_sum < ci2->_sum;
|
||||||
|
|
||||||
|
if (col==1) {
|
||||||
|
uint64 cc1 = ci1->_cc;
|
||||||
|
uint64 cc2 = ci2->_cc;
|
||||||
|
if (cc1 == 0) cc1 = 1;
|
||||||
|
if (cc2 == 0) cc2 = 1;
|
||||||
|
return (ci1->_sum / cc1) < (ci2->_sum / cc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col==2)
|
||||||
|
return ci1->_sum2 < ci2->_sum2;
|
||||||
|
|
||||||
|
if (col==3) {
|
||||||
|
uint64 cc1 = ci1->_cc;
|
||||||
|
uint64 cc2 = ci2->_cc;
|
||||||
|
if (cc1 == 0) cc1 = 1;
|
||||||
|
if (cc2 == 0) cc2 = 1;
|
||||||
|
return (ci1->_sum2 / cc1) < (ci2->_sum2 / cc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col==4)
|
||||||
|
return ci1->_cc < ci2->_cc;
|
||||||
|
|
||||||
|
return QTreeWidgetItem::operator <(other);
|
||||||
|
}
|
||||||
|
|
50
kcachegrind/libviews/callitem.h
Normal file
50
kcachegrind/libviews/callitem.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Items of call view.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CALLITEM_H
|
||||||
|
#define CALLITEM_H
|
||||||
|
|
||||||
|
#include <QTreeWidget>
|
||||||
|
#include "tracedata.h"
|
||||||
|
|
||||||
|
class CallView;
|
||||||
|
|
||||||
|
class CallItem: public QTreeWidgetItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CallItem(CallView*, QTreeWidget*, TraceCall* c);
|
||||||
|
|
||||||
|
bool operator<(const QTreeWidgetItem& other) const;
|
||||||
|
TraceCall* call() { return _call; }
|
||||||
|
CallView* view() { return _view; }
|
||||||
|
void updateCost();
|
||||||
|
void updateGroup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SubCost _sum, _sum2;
|
||||||
|
SubCost _cc;
|
||||||
|
TraceCall* _call;
|
||||||
|
CallView* _view;
|
||||||
|
TraceFunction *_active, *_shown;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
993
kcachegrind/libviews/callmapview.cpp
Normal file
993
kcachegrind/libviews/callmapview.cpp
Normal file
|
@ -0,0 +1,993 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call Map View
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "callmapview.h"
|
||||||
|
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "globalguiconfig.h"
|
||||||
|
#include "listutils.h"
|
||||||
|
#include "toplevelbase.h"
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// CallMapView
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
// defaults
|
||||||
|
#define DEFAULT_SPLITMODE "Rows"
|
||||||
|
#define DEFAULT_DRAWNAME true
|
||||||
|
#define DEFAULT_DRAWCOST true
|
||||||
|
#define DEFAULT_DRAWLOCATION false
|
||||||
|
#define DEFAULT_DRAWCALLS false
|
||||||
|
#define DEFAULT_FORCESTRINGS false
|
||||||
|
#define DEFAULT_ROTATION true
|
||||||
|
#define DEFAULT_SHADING true
|
||||||
|
#define DEFAULT_STOPNAME ""
|
||||||
|
#define DEFAULT_MAXDEPTH -1
|
||||||
|
#define DEFAULT_MAXAREA 100
|
||||||
|
|
||||||
|
|
||||||
|
CallMapView::CallMapView(bool showCallers, TraceItemView* parentView,
|
||||||
|
QWidget* parent, const char* name)
|
||||||
|
: TreeMapWidget(new CallMapBaseItem(), parent), TraceItemView(parentView)
|
||||||
|
{
|
||||||
|
setObjectName(name);
|
||||||
|
_showCallers = showCallers;
|
||||||
|
|
||||||
|
setFieldType(0, tr("A thing's name", "Name" ));
|
||||||
|
setFieldType(1, tr( "Cost" ));
|
||||||
|
setFieldType(2, tr( "Location" ));
|
||||||
|
setFieldPosition(2, TreeMapItem::TopLeft);
|
||||||
|
setFieldType(3, tr( "Calls" ));
|
||||||
|
setFieldPosition(3, TreeMapItem::TopRight);
|
||||||
|
|
||||||
|
setSplitMode(DEFAULT_SPLITMODE);
|
||||||
|
setFieldVisible(0, DEFAULT_DRAWNAME);
|
||||||
|
setFieldVisible(1, DEFAULT_DRAWCOST);
|
||||||
|
setFieldVisible(2, DEFAULT_DRAWLOCATION);
|
||||||
|
setFieldVisible(3, DEFAULT_DRAWCALLS);
|
||||||
|
setFieldForced(0, DEFAULT_FORCESTRINGS);
|
||||||
|
setFieldForced(1, DEFAULT_FORCESTRINGS);
|
||||||
|
setFieldForced(2, DEFAULT_FORCESTRINGS);
|
||||||
|
setFieldForced(3, DEFAULT_FORCESTRINGS);
|
||||||
|
setAllowRotation(DEFAULT_ROTATION);
|
||||||
|
setShadingEnabled(DEFAULT_SHADING);
|
||||||
|
setMinimalArea(DEFAULT_MAXAREA);
|
||||||
|
|
||||||
|
connect(this,
|
||||||
|
SIGNAL(doubleClicked(TreeMapItem*)),
|
||||||
|
SLOT(activatedSlot(TreeMapItem*)));
|
||||||
|
connect(this,
|
||||||
|
SIGNAL(returnPressed(TreeMapItem*)),
|
||||||
|
SLOT(activatedSlot(TreeMapItem*)));
|
||||||
|
connect(this,
|
||||||
|
SIGNAL(currentChanged(TreeMapItem*, bool)),
|
||||||
|
SLOT(selectedSlot(TreeMapItem*, bool)));
|
||||||
|
connect(this,
|
||||||
|
SIGNAL(contextMenuRequested(TreeMapItem*,const QPoint &)),
|
||||||
|
SLOT(context(TreeMapItem*,const QPoint &)));
|
||||||
|
|
||||||
|
this->setWhatsThis( whatsThis());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CallMapView::whatsThis() const
|
||||||
|
{
|
||||||
|
QString s = _showCallers ?
|
||||||
|
tr( "<b>Caller Map</b>"
|
||||||
|
"<p>This graph shows the nested hierarchy of "
|
||||||
|
"all callers of the current activated function. "
|
||||||
|
"Each colored rectangle represents a function; "
|
||||||
|
"its size tries to be proportional to the cost spent "
|
||||||
|
"therein while the active function is running "
|
||||||
|
"(however, there are drawing constraints).</p>") :
|
||||||
|
tr("<b>Call Map</b>"
|
||||||
|
"<p>This graph shows the nested hierarchy of "
|
||||||
|
"all callees of the current activated function. "
|
||||||
|
"Each colored rectangle represents a function; "
|
||||||
|
"its size tries to be proportional to the cost spent "
|
||||||
|
"therein while the active function is running "
|
||||||
|
"(however, there are drawing constraints).</p>");
|
||||||
|
|
||||||
|
s += tr( "<p>Appearance options can be found in the "
|
||||||
|
"in the context menu. To get exact size proportions, "
|
||||||
|
"choose 'Hide incorrect borders'. As this mode can be "
|
||||||
|
"<em>very</em> time consuming, you may want to limit "
|
||||||
|
"the maximum drawn nesting level before. "
|
||||||
|
"'Best' determinates the split direction for children "
|
||||||
|
"from the aspect ratio of the parent. "
|
||||||
|
"'Always Best' decides on remaining space for each "
|
||||||
|
"sibling. "
|
||||||
|
"'Ignore Proportions' takes space for function name "
|
||||||
|
"drawing <em>before</em> drawing children. Note that "
|
||||||
|
"size proportions can get <em>heavily</em> wrong.</p>"
|
||||||
|
|
||||||
|
"<p>This is a <em>TreeMap</em> widget. "
|
||||||
|
"Keyboard navigation is available with the left/right arrow "
|
||||||
|
"keys for traversing siblings, and up/down arrow keys "
|
||||||
|
"to go a nesting level up/down. "
|
||||||
|
"<em>Return</em> activates the current item.</p>");
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::setData(TraceData* d)
|
||||||
|
{
|
||||||
|
TraceItemView::setData(d);
|
||||||
|
|
||||||
|
((CallMapBaseItem*)base())->setFunction(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::addItemListMenu(QMenu* menu, TreeMapItem* item)
|
||||||
|
{
|
||||||
|
QAction* a;
|
||||||
|
|
||||||
|
QMenu* m = menu->addMenu(tr("Go To"));
|
||||||
|
int count = 0;
|
||||||
|
while (count<GlobalConfig::maxSymbolCount() && item) {
|
||||||
|
QString name = item->text(0);
|
||||||
|
a = m->addAction(GlobalConfig::shortenSymbol(name));
|
||||||
|
a->setData(QVariant::fromValue( (void*)item ));
|
||||||
|
item = item->parent();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
connect(m, SIGNAL(triggered(QAction*)),
|
||||||
|
this, SLOT(mapItemTriggered(QAction*)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::mapItemTriggered(QAction* a)
|
||||||
|
{
|
||||||
|
activatedSlot( (TreeMapItem*) a->data().value<void*>() );
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction* CallMapView::addDrawingDepthAction(QMenu* m,
|
||||||
|
const QString& s, int d)
|
||||||
|
{
|
||||||
|
QAction* a = m->addAction(s);
|
||||||
|
a->setData(d);
|
||||||
|
a->setCheckable(true);
|
||||||
|
a->setChecked(maxDrawingDepth() == d);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::addDrawingDepthMenu(QMenu* menu,
|
||||||
|
TreeMapItem* i, const QString& name)
|
||||||
|
{
|
||||||
|
QMenu* m = menu->addMenu(tr("Stop at Depth"));
|
||||||
|
addDrawingDepthAction(m, tr("No Depth Limit"), -1);
|
||||||
|
m->addSeparator();
|
||||||
|
addDrawingDepthAction(m, tr("Depth 10"), 10);
|
||||||
|
addDrawingDepthAction(m, tr("Depth 15"), 15);
|
||||||
|
addDrawingDepthAction(m, tr("Depth 20"), 20);
|
||||||
|
if (i) {
|
||||||
|
m->addSeparator();
|
||||||
|
addDrawingDepthAction(m, tr("Depth of '%1' (%2)")
|
||||||
|
.arg(name).arg(i->depth()),
|
||||||
|
i->depth());
|
||||||
|
}
|
||||||
|
int maxDepth = maxDrawingDepth();
|
||||||
|
if (maxDepth>0) {
|
||||||
|
m->addSeparator();
|
||||||
|
addDrawingDepthAction(m, tr("Decrement Depth (to %1)").arg(maxDepth-1),
|
||||||
|
maxDepth-1);
|
||||||
|
addDrawingDepthAction(m, tr("Increment Depth (to %1)").arg(maxDepth+1),
|
||||||
|
maxDepth+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(m, SIGNAL(triggered(QAction*)),
|
||||||
|
this, SLOT(drawingDepthTriggered(QAction*)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::drawingDepthTriggered(QAction* a)
|
||||||
|
{
|
||||||
|
setMaxDrawingDepth(a->data().toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction* CallMapView::addStopFunctionAction(QMenu* m,
|
||||||
|
const QString& s,
|
||||||
|
const QString& v)
|
||||||
|
{
|
||||||
|
QAction* a = m->addAction(s);
|
||||||
|
a->setData(v);
|
||||||
|
a->setCheckable(true);
|
||||||
|
a->setChecked(fieldStop(0) == v);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::addStopFunctionMenu(QMenu* menu, TreeMapItem* item)
|
||||||
|
{
|
||||||
|
QMenu* m = menu->addMenu(tr("Stop at Function"));
|
||||||
|
addStopFunctionAction(m, tr("No Function Limit"), QString());
|
||||||
|
|
||||||
|
bool foundStopName = false;
|
||||||
|
QAction* a;
|
||||||
|
if (item) {
|
||||||
|
m->addSeparator();
|
||||||
|
int count = 0;
|
||||||
|
while (count<GlobalConfig::maxSymbolCount() && item) {
|
||||||
|
QString name = GlobalConfig::shortenSymbol(item->text(0));
|
||||||
|
a = addStopFunctionAction(m, name, item->text(0));
|
||||||
|
if (a->isChecked()) foundStopName = true;
|
||||||
|
item = item->parent();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundStopName && !fieldStop(0).isEmpty()) {
|
||||||
|
m->addSeparator();
|
||||||
|
QString name = GlobalConfig::shortenSymbol(fieldStop(0));
|
||||||
|
addStopFunctionAction(m, name, fieldStop(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(m, SIGNAL(triggered(QAction*)),
|
||||||
|
this, SLOT(stopFunctionTriggered(QAction*)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::stopFunctionTriggered(QAction* a)
|
||||||
|
{
|
||||||
|
setFieldStop(0, a->data().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction* CallMapView::addAreaLimitAction(QMenu* m,
|
||||||
|
const QString& s, int v)
|
||||||
|
{
|
||||||
|
QAction* a = m->addAction(s);
|
||||||
|
a->setData(v);
|
||||||
|
a->setCheckable(true);
|
||||||
|
a->setChecked(minimalArea() == v);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::addAreaLimitMenu(QMenu* menu, TreeMapItem* i,
|
||||||
|
const QString& name)
|
||||||
|
{
|
||||||
|
QMenu* m = menu->addMenu(tr("Stop at Area"));
|
||||||
|
addAreaLimitAction(m, tr("No Area Limit"), -1);
|
||||||
|
m->addSeparator();
|
||||||
|
addAreaLimitAction(m, tr("100 Pixels"), 100);
|
||||||
|
addAreaLimitAction(m, tr("200 Pixels"), 200);
|
||||||
|
addAreaLimitAction(m, tr("500 Pixels"), 500);
|
||||||
|
addAreaLimitAction(m, tr("1000 Pixels"), 1000);
|
||||||
|
|
||||||
|
int currentArea = 0;
|
||||||
|
if (i) {
|
||||||
|
currentArea = i->width() * i->height();
|
||||||
|
m->addSeparator();
|
||||||
|
addAreaLimitAction(m, tr("Area of '%1' (%2)")
|
||||||
|
.arg(name).arg(currentArea), currentArea);
|
||||||
|
}
|
||||||
|
int mArea = minimalArea();
|
||||||
|
if (mArea>0) {
|
||||||
|
m->addSeparator();
|
||||||
|
addAreaLimitAction(m, tr("Double Area Limit (to %1)")
|
||||||
|
.arg(mArea*2), mArea*2);
|
||||||
|
addAreaLimitAction(m, tr("Half Area Limit (to %1)")
|
||||||
|
.arg(mArea/2), mArea/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(m, SIGNAL(triggered(QAction*)),
|
||||||
|
this, SLOT(areaLimitTriggered(QAction*)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::areaLimitTriggered(QAction* a)
|
||||||
|
{
|
||||||
|
setMinimalArea(a->data().toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction* CallMapView::addBorderWidthAction(QMenu* m, const QString& s, int v)
|
||||||
|
{
|
||||||
|
QAction* a = m->addAction(s);
|
||||||
|
a->setData(v);
|
||||||
|
a->setCheckable(true);
|
||||||
|
a->setChecked(borderWidth() == v);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::borderWidthTriggered(QAction* a)
|
||||||
|
{
|
||||||
|
setBorderWidth(a->data().toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::context(TreeMapItem* i,const QPoint & p)
|
||||||
|
{
|
||||||
|
if (!i) return;
|
||||||
|
|
||||||
|
QMenu popup;
|
||||||
|
QAction* a;
|
||||||
|
|
||||||
|
QString shortCurrentName;
|
||||||
|
if (i) {
|
||||||
|
shortCurrentName = GlobalConfig::shortenSymbol(i->text(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i) {
|
||||||
|
addItemListMenu(&popup, i);
|
||||||
|
popup.addSeparator();
|
||||||
|
}
|
||||||
|
addGoMenu(&popup);
|
||||||
|
popup.addSeparator();
|
||||||
|
addDrawingDepthMenu(&popup, i, shortCurrentName);
|
||||||
|
addStopFunctionMenu(&popup, i);
|
||||||
|
addAreaLimitMenu(&popup, i, shortCurrentName);
|
||||||
|
popup.addSeparator();
|
||||||
|
|
||||||
|
QMenu* vpopup = popup.addMenu(tr("Visualization"));
|
||||||
|
QMenu* spopup = vpopup->addMenu(tr("Split Direction"));
|
||||||
|
addSplitDirectionItems(spopup);
|
||||||
|
|
||||||
|
QAction* skipBorderAction = vpopup->addAction(tr("Skip Incorrect Borders"));
|
||||||
|
skipBorderAction->setEnabled(!_showCallers);
|
||||||
|
skipBorderAction->setCheckable(true);
|
||||||
|
skipBorderAction->setChecked(skipIncorrectBorder());
|
||||||
|
|
||||||
|
QMenu* bpopup = vpopup->addMenu(tr("Border Width"));
|
||||||
|
a = addBorderWidthAction(bpopup, tr("Border 0"), 0);
|
||||||
|
a->setEnabled(!_showCallers);
|
||||||
|
addBorderWidthAction(bpopup, tr("Border 1"), 1);
|
||||||
|
addBorderWidthAction(bpopup, tr("Border 2"), 2);
|
||||||
|
addBorderWidthAction(bpopup, tr("Border 3"), 3);
|
||||||
|
connect(bpopup, SIGNAL(triggered(QAction*)),
|
||||||
|
this, SLOT(borderWidthTriggered(QAction*)) );
|
||||||
|
vpopup->addSeparator();
|
||||||
|
|
||||||
|
QAction* drawNamesAction = vpopup->addAction(tr("Draw Symbol Names"));
|
||||||
|
drawNamesAction->setCheckable(true);
|
||||||
|
QAction* drawCostAction = vpopup->addAction(tr("Draw Cost"));
|
||||||
|
drawCostAction->setCheckable(true);
|
||||||
|
QAction* drawLocationAction = vpopup->addAction(tr("Draw Location"));
|
||||||
|
drawLocationAction->setCheckable(true);
|
||||||
|
QAction* drawCallsAction = vpopup->addAction(tr("Draw Calls"));
|
||||||
|
drawCallsAction->setCheckable(true);
|
||||||
|
vpopup->addSeparator();
|
||||||
|
|
||||||
|
QAction* ignorePropAction = vpopup->addAction(tr("Ignore Proportions"));
|
||||||
|
ignorePropAction->setCheckable(true);
|
||||||
|
QAction* allowRotationAction = vpopup->addAction(tr("Allow Rotation"));
|
||||||
|
allowRotationAction->setCheckable(true);
|
||||||
|
if (!fieldVisible(0) &&
|
||||||
|
!fieldVisible(1) &&
|
||||||
|
!fieldVisible(2) &&
|
||||||
|
!fieldVisible(3)) {
|
||||||
|
ignorePropAction->setEnabled(false);
|
||||||
|
allowRotationAction->setEnabled(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
drawNamesAction->setChecked(fieldVisible(0));
|
||||||
|
drawCostAction->setChecked(fieldVisible(1));
|
||||||
|
drawLocationAction->setChecked(fieldVisible(2));
|
||||||
|
drawCallsAction->setChecked(fieldVisible(3));
|
||||||
|
ignorePropAction->setChecked(fieldForced(0));
|
||||||
|
allowRotationAction->setChecked(allowRotation());
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction* drawShadingAction = vpopup->addAction(tr("Shading"));
|
||||||
|
drawShadingAction->setCheckable(true);
|
||||||
|
drawShadingAction->setChecked(isShadingEnabled());
|
||||||
|
|
||||||
|
a = popup.exec(mapToGlobal(p));
|
||||||
|
if (a == drawNamesAction)
|
||||||
|
setFieldVisible(0, !fieldVisible(0));
|
||||||
|
else if (a == drawCostAction)
|
||||||
|
setFieldVisible(1, !fieldVisible(1));
|
||||||
|
else if (a == drawLocationAction)
|
||||||
|
setFieldVisible(2, !fieldVisible(2));
|
||||||
|
else if (a == drawCallsAction)
|
||||||
|
setFieldVisible(3, !fieldVisible(3));
|
||||||
|
else if (a == ignorePropAction) {
|
||||||
|
bool newSetting = !fieldForced(0);
|
||||||
|
setFieldForced(0, newSetting);
|
||||||
|
setFieldForced(1, newSetting);
|
||||||
|
setFieldForced(2, newSetting);
|
||||||
|
setFieldForced(3, newSetting);
|
||||||
|
}
|
||||||
|
else if (a == allowRotationAction)
|
||||||
|
setAllowRotation(!allowRotation());
|
||||||
|
else if (a == drawShadingAction)
|
||||||
|
setShadingEnabled(!isShadingEnabled());
|
||||||
|
else if (a == skipBorderAction)
|
||||||
|
setSkipIncorrectBorder(!skipIncorrectBorder());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::activatedSlot(TreeMapItem* item)
|
||||||
|
{
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
if (item->rtti() == 1) {
|
||||||
|
CallMapBaseItem* bi = (CallMapBaseItem*)item;
|
||||||
|
activated(bi->function());
|
||||||
|
}
|
||||||
|
else if (item->rtti() == 2) {
|
||||||
|
CallMapCallingItem* ci = (CallMapCallingItem*)item;
|
||||||
|
activated(ci->function());
|
||||||
|
}
|
||||||
|
else if (item->rtti() == 3) {
|
||||||
|
CallMapCallerItem* ci = (CallMapCallerItem*)item;
|
||||||
|
activated(ci->function());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::selectedSlot(TreeMapItem* item, bool kbd)
|
||||||
|
{
|
||||||
|
if (!item) return;
|
||||||
|
if (item->text(0).isEmpty()) return;
|
||||||
|
|
||||||
|
if (kbd) {
|
||||||
|
QString msg = tr("Call Map: Current is '%1'").arg(item->text(0));
|
||||||
|
if (_topLevel)
|
||||||
|
_topLevel->showMessage(msg, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceFunction* f = 0;
|
||||||
|
|
||||||
|
if (item->rtti() == 1) {
|
||||||
|
CallMapBaseItem* bi = (CallMapBaseItem*)item;
|
||||||
|
f = bi->function();
|
||||||
|
}
|
||||||
|
else if (item->rtti() == 2) {
|
||||||
|
CallMapCallingItem* ci = (CallMapCallingItem*)item;
|
||||||
|
f = ci->function();
|
||||||
|
}
|
||||||
|
else if (item->rtti() == 3) {
|
||||||
|
CallMapCallerItem* ci = (CallMapCallerItem*)item;
|
||||||
|
f = ci->function();
|
||||||
|
}
|
||||||
|
if (f) {
|
||||||
|
// this avoids marking
|
||||||
|
_selectedItem = f;
|
||||||
|
selected(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CostItem* CallMapView::canShow(CostItem* i)
|
||||||
|
{
|
||||||
|
ProfileContext::Type t = i ? i->type() : ProfileContext::InvalidType;
|
||||||
|
|
||||||
|
switch(t) {
|
||||||
|
case ProfileContext::Function:
|
||||||
|
case ProfileContext::FunctionCycle:
|
||||||
|
return i;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::doUpdate(int changeType, bool)
|
||||||
|
{
|
||||||
|
if (changeType == eventType2Changed) return;
|
||||||
|
|
||||||
|
// if there is a selected item, always draw marking...
|
||||||
|
if (changeType & selectedItemChanged) {
|
||||||
|
TraceFunction* f = 0;
|
||||||
|
|
||||||
|
if (_selectedItem) {
|
||||||
|
switch(_selectedItem->type()) {
|
||||||
|
case ProfileContext::Function:
|
||||||
|
case ProfileContext::FunctionCycle:
|
||||||
|
f = (TraceFunction*)_selectedItem;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if this is the only change...
|
||||||
|
if (changeType == selectedItemChanged) {
|
||||||
|
setMarked(f ? 1:0, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setMarked(f ? 1:0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (changeType & activeItemChanged) {
|
||||||
|
TraceFunction* f = 0;
|
||||||
|
|
||||||
|
if (_activeItem) {
|
||||||
|
switch(_activeItem->type()) {
|
||||||
|
case ProfileContext::Function:
|
||||||
|
case ProfileContext::FunctionCycle:
|
||||||
|
f = (TraceFunction*)_activeItem;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
((CallMapBaseItem*)base())->setFunction(f);
|
||||||
|
}
|
||||||
|
else if ( ((changeType & partsChanged) && GlobalConfig::showCycles()) ||
|
||||||
|
(changeType & dataChanged) ||
|
||||||
|
(changeType & configChanged)) {
|
||||||
|
/* regenerates the treemap because traceitems were added/removed */
|
||||||
|
base()->refresh();
|
||||||
|
}
|
||||||
|
else if ((changeType & partsChanged) ||
|
||||||
|
(changeType & eventTypeChanged)) {
|
||||||
|
/* we need to do the draw order sorting again as the values change */
|
||||||
|
resort();
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QColor CallMapView::groupColor(TraceFunction* f) const
|
||||||
|
{
|
||||||
|
if (!f)
|
||||||
|
return palette().color( QPalette::Button );
|
||||||
|
|
||||||
|
return GlobalGUIConfig::functionColor(_groupType, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString CallMapView::tipString(TreeMapItem* i) const
|
||||||
|
{
|
||||||
|
QString tip, itemTip;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
//qDebug("CallMapView::tipString for '%s'", i->text(0).toAscii());
|
||||||
|
|
||||||
|
// first, SubPartItem's
|
||||||
|
while (i && count<GlobalConfig::maxSymbolCount()) {
|
||||||
|
itemTip = GlobalConfig::shortenSymbol(i->text(0));
|
||||||
|
|
||||||
|
if (!i->text(1).isEmpty())
|
||||||
|
itemTip += " (" + i->text(1) + ')';
|
||||||
|
|
||||||
|
if (!tip.isEmpty()) tip += '\n';
|
||||||
|
|
||||||
|
tip += itemTip;
|
||||||
|
i = i->parent();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (count == GlobalConfig::maxSymbolCount()) tip += "\n...";
|
||||||
|
|
||||||
|
return tip;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ProfileCostArray* CallMapView::totalCost()
|
||||||
|
{
|
||||||
|
TraceFunction* f = ((CallMapBaseItem*)base())->function();
|
||||||
|
if (!f) return 0;
|
||||||
|
|
||||||
|
return GlobalConfig::showExpanded() ? f->inclusive() : f->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// CallMapBaseItem
|
||||||
|
|
||||||
|
CallMapBaseItem::CallMapBaseItem()
|
||||||
|
{
|
||||||
|
_f = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapBaseItem::setFunction(TraceFunction* f)
|
||||||
|
{
|
||||||
|
if (f == _f) return;
|
||||||
|
|
||||||
|
_f = f;
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString CallMapBaseItem::text(int textNo) const
|
||||||
|
{
|
||||||
|
if (textNo == 0) {
|
||||||
|
if (!_f)
|
||||||
|
return QObject::tr("(no function)");
|
||||||
|
|
||||||
|
return _f->prettyName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_f) return QString();
|
||||||
|
|
||||||
|
if (textNo == 2) return _f->prettyLocation();
|
||||||
|
if (textNo == 3) return _f->calledCount().pretty();
|
||||||
|
if (textNo != 1) return QString();
|
||||||
|
|
||||||
|
EventType* ct = ((CallMapView*)widget())->eventType();
|
||||||
|
ProfileCostArray* t = ((CallMapView*)widget())->totalCost();
|
||||||
|
|
||||||
|
if (GlobalConfig::showPercentage()) {
|
||||||
|
double sum, total = t->subCost(ct);
|
||||||
|
if (total == 0.0)
|
||||||
|
sum = 100.0;
|
||||||
|
else
|
||||||
|
sum = 100.0 * _f->inclusive()->subCost(ct) / total;
|
||||||
|
|
||||||
|
return QString("%1 %")
|
||||||
|
.arg(sum, 0, 'f', GlobalConfig::percentPrecision());
|
||||||
|
}
|
||||||
|
return _f->inclusive()->prettySubCost(ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap CallMapBaseItem::pixmap(int i) const
|
||||||
|
{
|
||||||
|
if ((i != 1) || !_f) return QPixmap();
|
||||||
|
|
||||||
|
EventType* ct = ((CallMapView*)widget())->eventType();
|
||||||
|
ProfileCostArray* t = ((CallMapView*)widget())->totalCost();
|
||||||
|
|
||||||
|
// colored level meter with frame
|
||||||
|
return costPixmap( ct, _f->inclusive(), (double) (t->subCost(ct)), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double CallMapBaseItem::value() const
|
||||||
|
{
|
||||||
|
if (!_f) return 0.0;
|
||||||
|
|
||||||
|
EventType* ct;
|
||||||
|
ct = ((CallMapView*)widget())->eventType();
|
||||||
|
return (double) _f->inclusive()->subCost(ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double CallMapBaseItem::sum() const
|
||||||
|
{
|
||||||
|
if (!_f) return 0.0;
|
||||||
|
|
||||||
|
CallMapView* w = (CallMapView*)widget();
|
||||||
|
|
||||||
|
if (w->showCallers())
|
||||||
|
return 0.0;
|
||||||
|
else
|
||||||
|
return (double) _f->inclusive()->subCost(w->eventType());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CallMapBaseItem::isMarked(int) const
|
||||||
|
{
|
||||||
|
return ((CallMapView*)widget())->selectedItem() == _f;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeMapItemList* CallMapBaseItem::children()
|
||||||
|
{
|
||||||
|
if (_f && !initialized()) {
|
||||||
|
CallMapView* w = (CallMapView*)widget();
|
||||||
|
|
||||||
|
if (0) qDebug("Create Function %s (%s)",
|
||||||
|
w->showCallers() ? "Callers":"Callees",
|
||||||
|
qPrintable(text(0)));
|
||||||
|
|
||||||
|
setSorting(-1);
|
||||||
|
if (w->showCallers()) {
|
||||||
|
foreach(TraceCall* call, _f->callers()) {
|
||||||
|
// do not show calls inside of a cycle
|
||||||
|
if (call->inCycle()>0) continue;
|
||||||
|
if (call->isRecursion()) continue;
|
||||||
|
|
||||||
|
addItem(new CallMapCallerItem(1.0, call));
|
||||||
|
}
|
||||||
|
|
||||||
|
setSum(0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foreach(TraceCall* call, _f->callings()) {
|
||||||
|
// do not show calls inside of a cycle
|
||||||
|
if (call->inCycle()>0) continue;
|
||||||
|
if (call->isRecursion()) continue;
|
||||||
|
|
||||||
|
CallMapCallingItem* i = new CallMapCallingItem(1.0, call);
|
||||||
|
i->init();
|
||||||
|
addItem(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSum(_f->inclusive()->subCost(w->eventType()));
|
||||||
|
}
|
||||||
|
setSorting(-2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _children;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor CallMapBaseItem::backColor() const
|
||||||
|
{
|
||||||
|
return ((CallMapView*)widget())->groupColor(_f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// CallMapCallingItems
|
||||||
|
|
||||||
|
CallMapCallingItem::CallMapCallingItem(double factor, TraceCall* c)
|
||||||
|
{
|
||||||
|
_factor = factor;
|
||||||
|
_c = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapCallingItem::init()
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
// create association: if not possible, i.e. an ass. already exists
|
||||||
|
// for the function, we need to draw the recursive version
|
||||||
|
_recursive = !setFunction(_c->called());
|
||||||
|
_valid = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CallMapCallingItem::text(int textNo) const
|
||||||
|
{
|
||||||
|
if (textNo == 0) {
|
||||||
|
if (!_c)
|
||||||
|
return QObject::tr("(no call)");
|
||||||
|
|
||||||
|
return _c->calledName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textNo == 2) return _c->called()->prettyLocation();
|
||||||
|
if (textNo == 3) return SubCost(_factor * _c->callCount()).pretty();
|
||||||
|
if (textNo != 1) return QString();
|
||||||
|
|
||||||
|
EventType* ct;
|
||||||
|
ct = ((CallMapView*)widget())->eventType();
|
||||||
|
|
||||||
|
SubCost val = SubCost(_factor * _c->subCost(ct));
|
||||||
|
if (GlobalConfig::showPercentage()) {
|
||||||
|
// percentage relative to function cost
|
||||||
|
ProfileCostArray* t = ((CallMapView*)widget())->totalCost();
|
||||||
|
double p = 100.0 * _factor * _c->subCost(ct) / t->subCost(ct);
|
||||||
|
return QString("%1 %")
|
||||||
|
.arg(p, 0, 'f', GlobalConfig::percentPrecision());
|
||||||
|
}
|
||||||
|
return val.pretty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap CallMapCallingItem::pixmap(int i) const
|
||||||
|
{
|
||||||
|
if (i != 1) return QPixmap();
|
||||||
|
|
||||||
|
// Cost pixmap
|
||||||
|
EventType* ct = ((CallMapView*)widget())->eventType();
|
||||||
|
ProfileCostArray* t = ((CallMapView*)widget())->totalCost();
|
||||||
|
|
||||||
|
// colored level meter with frame
|
||||||
|
return costPixmap( ct, _c, t->subCost(ct) / _factor, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double CallMapCallingItem::value() const
|
||||||
|
{
|
||||||
|
EventType* ct;
|
||||||
|
ct = ((CallMapView*)widget())->eventType();
|
||||||
|
return _factor * _c->subCost(ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
double CallMapCallingItem::sum() const
|
||||||
|
{
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CallMapCallingItem::isMarked(int) const
|
||||||
|
{
|
||||||
|
return ((CallMapView*)widget())->selectedItem() == _c->called();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TreeMapItemList* CallMapCallingItem::children()
|
||||||
|
{
|
||||||
|
if (!initialized()) {
|
||||||
|
if (0) qDebug("Create Calling subitems (%s)",
|
||||||
|
qPrintable(path(0).join("/")));
|
||||||
|
|
||||||
|
EventType* ct;
|
||||||
|
ct = ((CallMapView*)widget())->eventType();
|
||||||
|
|
||||||
|
// same as sum()
|
||||||
|
SubCost s = _c->called()->inclusive()->subCost(ct);
|
||||||
|
SubCost v = _c->subCost(ct);
|
||||||
|
if (v>s) {
|
||||||
|
qDebug("Warning: CallingItem subVal %u > Sum %u (%s)",
|
||||||
|
(unsigned)v, (unsigned)s, qPrintable(_c->called()->prettyName()));
|
||||||
|
v = s;
|
||||||
|
}
|
||||||
|
double newFactor = _factor * v / s;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
qDebug("CallingItem: Subitems of %s => %s, factor %f * %d/%d => %f",
|
||||||
|
qPrintable(_c->caller()->prettyName()),
|
||||||
|
qPrintable(_c->called()->prettyName()),
|
||||||
|
_factor, v, s, newFactor);
|
||||||
|
#endif
|
||||||
|
setSorting(-1);
|
||||||
|
foreach(TraceCall* call, _c->called()->callings()) {
|
||||||
|
// do not show calls inside of a cycle
|
||||||
|
if (call->inCycle()>0) continue;
|
||||||
|
if (call->isRecursion()) continue;
|
||||||
|
|
||||||
|
CallMapCallingItem* i = new CallMapCallingItem(newFactor, call);
|
||||||
|
i->init();
|
||||||
|
addItem(i);
|
||||||
|
}
|
||||||
|
setSorting(-2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _children;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QColor CallMapCallingItem::backColor() const
|
||||||
|
{
|
||||||
|
CallMapView* w = (CallMapView*)widget();
|
||||||
|
return w->groupColor(_c->called());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// CallMapCallerItem
|
||||||
|
|
||||||
|
CallMapCallerItem::CallMapCallerItem(double factor, TraceCall* c)
|
||||||
|
{
|
||||||
|
_factor = factor;
|
||||||
|
_c = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CallMapCallerItem::text(int textNo) const
|
||||||
|
{
|
||||||
|
if (textNo == 0) {
|
||||||
|
if (!_c)
|
||||||
|
return QObject::tr("(no call)");
|
||||||
|
|
||||||
|
return _c->callerName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textNo == 2) return _c->caller()->prettyLocation();
|
||||||
|
if (textNo == 3) return SubCost(_factor * _c->callCount()).pretty();
|
||||||
|
if (textNo != 1) return QString();
|
||||||
|
|
||||||
|
EventType* ct;
|
||||||
|
ct = ((CallMapView*)widget())->eventType();
|
||||||
|
|
||||||
|
SubCost val = SubCost(_factor * _c->subCost(ct));
|
||||||
|
if (GlobalConfig::showPercentage()) {
|
||||||
|
ProfileCostArray* t = ((CallMapView*)widget())->totalCost();
|
||||||
|
double p = 100.0 * _factor * _c->subCost(ct) / t->subCost(ct);
|
||||||
|
return QString("%1 %")
|
||||||
|
.arg(p, 0, 'f', GlobalConfig::percentPrecision());
|
||||||
|
}
|
||||||
|
return val.pretty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QPixmap CallMapCallerItem::pixmap(int i) const
|
||||||
|
{
|
||||||
|
if (i != 1) return QPixmap();
|
||||||
|
|
||||||
|
// Cost pixmap
|
||||||
|
EventType* ct = ((CallMapView*)widget())->eventType();
|
||||||
|
ProfileCostArray* t = ((CallMapView*)widget())->totalCost();
|
||||||
|
|
||||||
|
// colored level meter with frame
|
||||||
|
return costPixmap( ct, _c, t->subCost(ct) / _factor, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double CallMapCallerItem::value() const
|
||||||
|
{
|
||||||
|
EventType* ct;
|
||||||
|
ct = ((CallMapView*)widget())->eventType();
|
||||||
|
return (double) _c->subCost(ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CallMapCallerItem::isMarked(int) const
|
||||||
|
{
|
||||||
|
return ((CallMapView*)widget())->selectedItem() == _c->caller();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TreeMapItemList* CallMapCallerItem::children()
|
||||||
|
{
|
||||||
|
if (!initialized()) {
|
||||||
|
//qDebug("Create Caller subitems (%s)", name().toAscii());
|
||||||
|
|
||||||
|
EventType* ct;
|
||||||
|
ct = ((CallMapView*)widget())->eventType();
|
||||||
|
|
||||||
|
SubCost s = _c->caller()->inclusive()->subCost(ct);
|
||||||
|
SubCost v = _c->subCost(ct);
|
||||||
|
double newFactor = _factor * v / s;
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
qDebug("CallerItem: Subitems of %s => %s, factor %f * %d/%d => %f",
|
||||||
|
qPrintable(_c->caller()->prettyName()),
|
||||||
|
qPrintable(_c->called()->prettyName()),
|
||||||
|
_factor, v, s, newFactor);
|
||||||
|
#endif
|
||||||
|
setSorting(-1);
|
||||||
|
|
||||||
|
foreach(TraceCall* call, _c->caller()->callers()) {
|
||||||
|
// do not show calls inside of a cycle
|
||||||
|
if (call->inCycle()>0) continue;
|
||||||
|
if (call->isRecursion()) continue;
|
||||||
|
|
||||||
|
TreeMapItem* i = new CallMapCallerItem(newFactor, call);
|
||||||
|
addItem(i);
|
||||||
|
}
|
||||||
|
setSorting(-2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _children;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor CallMapCallerItem::backColor() const
|
||||||
|
{
|
||||||
|
CallMapView* w = (CallMapView*)widget();
|
||||||
|
return w->groupColor(_c->caller());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::restoreOptions(const QString& prefix, const QString& postfix)
|
||||||
|
{
|
||||||
|
ConfigGroup* g = ConfigStorage::group(prefix, postfix);
|
||||||
|
|
||||||
|
setSplitMode(g->value("SplitMode", QString(DEFAULT_SPLITMODE)).toString());
|
||||||
|
|
||||||
|
setFieldVisible(0, g->value("DrawName", DEFAULT_DRAWNAME).toBool());
|
||||||
|
setFieldVisible(1, g->value("DrawCost", DEFAULT_DRAWCOST).toBool());
|
||||||
|
setFieldVisible(2, g->value("DrawLocation", DEFAULT_DRAWLOCATION).toBool());
|
||||||
|
setFieldVisible(3, g->value("DrawCalls", DEFAULT_DRAWCALLS).toBool());
|
||||||
|
|
||||||
|
bool enable = g->value("ForceStrings", DEFAULT_FORCESTRINGS).toBool();
|
||||||
|
setFieldForced(0, enable);
|
||||||
|
setFieldForced(1, enable);
|
||||||
|
setFieldForced(2, enable);
|
||||||
|
setFieldForced(3, enable);
|
||||||
|
|
||||||
|
setAllowRotation(g->value("AllowRotation", DEFAULT_ROTATION).toBool());
|
||||||
|
setShadingEnabled(g->value("Shading", DEFAULT_SHADING).toBool());
|
||||||
|
setFieldStop(0, g->value("StopName", QString(DEFAULT_STOPNAME)).toString());
|
||||||
|
setMaxDrawingDepth(g->value("MaxDepth", DEFAULT_MAXDEPTH).toInt());
|
||||||
|
setMinimalArea(g->value("MaxArea", DEFAULT_MAXAREA).toInt());
|
||||||
|
|
||||||
|
delete g;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallMapView::saveOptions(const QString& prefix, const QString& postfix)
|
||||||
|
{
|
||||||
|
ConfigGroup* g = ConfigStorage::group(prefix + postfix);
|
||||||
|
|
||||||
|
g->setValue("SplitMode", splitModeString(), QString(DEFAULT_SPLITMODE));
|
||||||
|
g->setValue("DrawName", fieldVisible(0), DEFAULT_DRAWNAME);
|
||||||
|
g->setValue("DrawCost", fieldVisible(1), DEFAULT_DRAWCOST);
|
||||||
|
g->setValue("DrawLocation", fieldVisible(2), DEFAULT_DRAWLOCATION);
|
||||||
|
g->setValue("DrawCalls", fieldVisible(3), DEFAULT_DRAWCALLS);
|
||||||
|
// when option for all text (0-3)
|
||||||
|
g->setValue("ForceStrings", fieldForced(0), DEFAULT_FORCESTRINGS);
|
||||||
|
|
||||||
|
g->setValue("AllowRotation", allowRotation(), DEFAULT_ROTATION);
|
||||||
|
g->setValue("Shading", isShadingEnabled(), DEFAULT_SHADING);
|
||||||
|
|
||||||
|
g->setValue("StopName", fieldStop(0), QString(DEFAULT_STOPNAME));
|
||||||
|
g->setValue("MaxDepth", maxDrawingDepth(), DEFAULT_MAXDEPTH);
|
||||||
|
g->setValue("MaxArea", minimalArea(), DEFAULT_MAXAREA);
|
||||||
|
|
||||||
|
delete g;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "moc_callmapview.cpp"
|
149
kcachegrind/libviews/callmapview.h
Normal file
149
kcachegrind/libviews/callmapview.h
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call Map View
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CALLMAPVIEW_H
|
||||||
|
#define CALLMAPVIEW_H
|
||||||
|
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
#include "treemap.h"
|
||||||
|
#include "tracedata.h"
|
||||||
|
#include "traceitemview.h"
|
||||||
|
|
||||||
|
class QAction;
|
||||||
|
class QMenu;
|
||||||
|
|
||||||
|
class CallMapView: public TreeMapWidget, public TraceItemView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
CallMapView(bool showCallers, TraceItemView* parentView,
|
||||||
|
QWidget* parent=0, const char* name=0);
|
||||||
|
|
||||||
|
QWidget* widget() { return this; }
|
||||||
|
QString whatsThis() const;
|
||||||
|
void setData(TraceData*);
|
||||||
|
|
||||||
|
void restoreOptions(const QString& prefix, const QString& postfix);
|
||||||
|
void saveOptions(const QString& prefix, const QString& postfix);
|
||||||
|
|
||||||
|
bool showCallers() const { return _showCallers; }
|
||||||
|
ProfileCostArray* totalCost();
|
||||||
|
QString tipString(TreeMapItem*) const;
|
||||||
|
QColor groupColor(TraceFunction*) const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void context(TreeMapItem*,const QPoint &);
|
||||||
|
void selectedSlot(TreeMapItem*, bool);
|
||||||
|
void activatedSlot(TreeMapItem*);
|
||||||
|
void mapItemTriggered(QAction*);
|
||||||
|
void drawingDepthTriggered(QAction*);
|
||||||
|
void stopFunctionTriggered(QAction*);
|
||||||
|
void areaLimitTriggered(QAction*);
|
||||||
|
void borderWidthTriggered(QAction*);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CostItem* canShow(CostItem*);
|
||||||
|
void doUpdate(int, bool);
|
||||||
|
|
||||||
|
// context menu builders
|
||||||
|
void addItemListMenu(QMenu*,TreeMapItem*);
|
||||||
|
QAction* addDrawingDepthAction(QMenu*, const QString&, int);
|
||||||
|
void addDrawingDepthMenu(QMenu*, TreeMapItem*, const QString&);
|
||||||
|
QAction* addStopFunctionAction(QMenu*, const QString&, const QString&);
|
||||||
|
void addStopFunctionMenu(QMenu*, TreeMapItem*);
|
||||||
|
QAction* addAreaLimitAction(QMenu*, const QString&, int);
|
||||||
|
void addAreaLimitMenu(QMenu*, TreeMapItem*, const QString&);
|
||||||
|
QAction* addBorderWidthAction(QMenu*, const QString&, int);
|
||||||
|
|
||||||
|
bool _showCallers;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Subitems of CallMap
|
||||||
|
|
||||||
|
class CallMapBaseItem: public TreeMapItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CallMapBaseItem();
|
||||||
|
|
||||||
|
void setFunction(TraceFunction* f);
|
||||||
|
TraceFunction* function() { return _f; }
|
||||||
|
int rtti() const { return 1; }
|
||||||
|
double sum() const;
|
||||||
|
double value() const ;
|
||||||
|
bool isMarked(int) const;
|
||||||
|
QString text(int) const;
|
||||||
|
QPixmap pixmap(int) const;
|
||||||
|
TreeMapItemList* children();
|
||||||
|
QColor backColor() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TraceFunction* _f;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CallMapCallingItem: public TreeMapItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CallMapCallingItem(double factor, TraceCall* c);
|
||||||
|
void init();
|
||||||
|
int rtti() const { return 2; }
|
||||||
|
int borderWidth() const { return widget()->borderWidth(); }
|
||||||
|
TraceFunction* function() { return _c->called(); }
|
||||||
|
double value() const;
|
||||||
|
double sum() const;
|
||||||
|
bool isMarked(int) const;
|
||||||
|
QString text(int) const;
|
||||||
|
QPixmap pixmap(int) const;
|
||||||
|
TreeMapItemList* children();
|
||||||
|
QColor backColor() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TraceCall* _c;
|
||||||
|
double _factor;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CallMapCallerItem: public TreeMapItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CallMapCallerItem(double factor, TraceCall* c);
|
||||||
|
int rtti() const { return 3; }
|
||||||
|
int borderWidth() const { return widget()->borderWidth(); }
|
||||||
|
TraceFunction* function() { return _c->caller(); }
|
||||||
|
double value() const;
|
||||||
|
bool isMarked(int) const;
|
||||||
|
QString text(int) const;
|
||||||
|
QPixmap pixmap(int) const;
|
||||||
|
TreeMapItemList* children();
|
||||||
|
QColor backColor() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TraceCall* _c;
|
||||||
|
double _factor;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
301
kcachegrind/libviews/callview.cpp
Normal file
301
kcachegrind/libviews/callview.cpp
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003-2009 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call Views
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "callview.h"
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QTreeWidget>
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
#include "globalconfig.h"
|
||||||
|
#include "callitem.h"
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// CallView
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
CallView::CallView(bool showCallers, TraceItemView* parentView, QWidget* parent)
|
||||||
|
: QTreeWidget(parent), TraceItemView(parentView)
|
||||||
|
{
|
||||||
|
_showCallers = showCallers;
|
||||||
|
|
||||||
|
QStringList headerLabels;
|
||||||
|
headerLabels << tr( "Cost" )
|
||||||
|
<< tr( "Cost per call" )
|
||||||
|
<< tr( "Cost 2" )
|
||||||
|
<< tr( "Cost 2 per call" )
|
||||||
|
<< tr( "Count" )
|
||||||
|
<< ((_showCallers) ? tr( "Caller" ) : tr( "Callee" ));
|
||||||
|
setHeaderLabels(headerLabels);
|
||||||
|
|
||||||
|
// forbid scaling icon pixmaps to smaller size
|
||||||
|
setIconSize(QSize(99,99));
|
||||||
|
setAllColumnsShowFocus(true);
|
||||||
|
setRootIsDecorated(false);
|
||||||
|
setUniformRowHeights(true);
|
||||||
|
// sorting will be enabled after refresh()
|
||||||
|
sortByColumn(0, Qt::DescendingOrder);
|
||||||
|
setMinimumHeight(50);
|
||||||
|
|
||||||
|
this->setWhatsThis( whatsThis() );
|
||||||
|
|
||||||
|
connect( this,
|
||||||
|
SIGNAL( currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
|
||||||
|
SLOT( selectedSlot(QTreeWidgetItem*,QTreeWidgetItem*) ) );
|
||||||
|
|
||||||
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect( this,
|
||||||
|
SIGNAL(customContextMenuRequested(const QPoint &) ),
|
||||||
|
SLOT(context(const QPoint &)));
|
||||||
|
|
||||||
|
connect(this,
|
||||||
|
SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
|
||||||
|
SLOT(activatedSlot(QTreeWidgetItem*,int)));
|
||||||
|
|
||||||
|
connect(header(), SIGNAL(sectionClicked(int)),
|
||||||
|
this, SLOT(headerClicked(int)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CallView::whatsThis() const
|
||||||
|
{
|
||||||
|
return _showCallers ?
|
||||||
|
tr( "<b>List of direct Callers</b>"
|
||||||
|
"<p>This list shows all functions calling the "
|
||||||
|
"current selected one directly, together with "
|
||||||
|
"a call count and the cost spent in the current "
|
||||||
|
"selected function while being called from the "
|
||||||
|
"function from the list.</p>"
|
||||||
|
"<p>An icon instead of an inclusive cost specifies "
|
||||||
|
"that this is a call inside of a recursive cycle. "
|
||||||
|
"An inclusive cost makes no sense here.</p>"
|
||||||
|
"<p>Selecting a function makes it the current selected "
|
||||||
|
"one of this information panel. "
|
||||||
|
"If there are two panels (Split mode), the "
|
||||||
|
"function of the other panel is changed instead.</p>") :
|
||||||
|
tr( "<b>List of direct Callees</b>"
|
||||||
|
"<p>This list shows all functions called by the "
|
||||||
|
"current selected one directly, together with "
|
||||||
|
"a call count and the cost spent in this function "
|
||||||
|
"while being called from the selected function.</p>"
|
||||||
|
"<p>Selecting a function makes it the current selected "
|
||||||
|
"one of this information panel. "
|
||||||
|
"If there are two panels (Split mode), the "
|
||||||
|
"function of the other panel is changed instead.</p>");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CallView::context(const QPoint & p)
|
||||||
|
{
|
||||||
|
QMenu popup;
|
||||||
|
|
||||||
|
// p is in local coordinates
|
||||||
|
int col = columnAt(p.x());
|
||||||
|
QTreeWidgetItem* i = itemAt(p);
|
||||||
|
TraceCall* c = i ? ((CallItem*) i)->call() : 0;
|
||||||
|
TraceFunction *f = 0, *cycle = 0;
|
||||||
|
|
||||||
|
QAction* activateFunctionAction = 0;
|
||||||
|
QAction* activateCycleAction = 0;
|
||||||
|
if (c) {
|
||||||
|
QString name = _showCallers ? c->callerName(true) : c->calledName(true);
|
||||||
|
f = _showCallers ? c->caller(true) : c->called(true);
|
||||||
|
cycle = f->cycle();
|
||||||
|
|
||||||
|
QString menuText = tr("Go to '%1'").arg(GlobalConfig::shortenSymbol(name));
|
||||||
|
activateFunctionAction = popup.addAction(menuText);
|
||||||
|
|
||||||
|
if (cycle) {
|
||||||
|
name = GlobalConfig::shortenSymbol(cycle->prettyName());
|
||||||
|
QString menuText = tr("Go to '%1'").arg(name);
|
||||||
|
activateCycleAction = popup.addAction(menuText);
|
||||||
|
}
|
||||||
|
|
||||||
|
popup.addSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
// add menu items to select event type if column displays cost for a type
|
||||||
|
if (col < 4) {
|
||||||
|
addEventTypeMenu(&popup);
|
||||||
|
popup.addSeparator();
|
||||||
|
}
|
||||||
|
addGoMenu(&popup);
|
||||||
|
|
||||||
|
QAction* a = popup.exec(mapToGlobal(p + QPoint(0,header()->height())));
|
||||||
|
if (a == activateFunctionAction)
|
||||||
|
TraceItemView::activated(f);
|
||||||
|
else if (a == activateCycleAction)
|
||||||
|
TraceItemView::activated(cycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallView::selectedSlot(QTreeWidgetItem * i, QTreeWidgetItem *)
|
||||||
|
{
|
||||||
|
if (!i) return;
|
||||||
|
TraceCall* c = ((CallItem*) i)->call();
|
||||||
|
// Should we skip cycles here?
|
||||||
|
CostItem* f = _showCallers ? c->caller(false) : c->called(false);
|
||||||
|
|
||||||
|
_selectedItem = f;
|
||||||
|
selected(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallView::activatedSlot(QTreeWidgetItem* i,int)
|
||||||
|
{
|
||||||
|
if (!i) return;
|
||||||
|
|
||||||
|
TraceCall* c = ((CallItem*) i)->call();
|
||||||
|
// skip cycles: use the context menu to get to the cycle...
|
||||||
|
CostItem* f = _showCallers ? c->caller(true) : c->called(true);
|
||||||
|
|
||||||
|
TraceItemView::activated(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallView::headerClicked(int col)
|
||||||
|
{
|
||||||
|
// name columns should be sortable in both ways
|
||||||
|
if (col == 5) return;
|
||||||
|
|
||||||
|
// all others only descending
|
||||||
|
sortByColumn(col, Qt::DescendingOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallView::keyPressEvent(QKeyEvent* event)
|
||||||
|
{
|
||||||
|
QTreeWidgetItem *item = currentItem();
|
||||||
|
if (item && ((event->key() == Qt::Key_Return) ||
|
||||||
|
(event->key() == Qt::Key_Space)))
|
||||||
|
{
|
||||||
|
TraceCall* c = ((CallItem*) item)->call();
|
||||||
|
CostItem* f = _showCallers ? c->caller(false) : c->called(false);
|
||||||
|
|
||||||
|
TraceItemView::activated(f);
|
||||||
|
}
|
||||||
|
QTreeView::keyPressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
CostItem* CallView::canShow(CostItem* i)
|
||||||
|
{
|
||||||
|
ProfileContext::Type t = i ? i->type() : ProfileContext::InvalidType;
|
||||||
|
|
||||||
|
switch(t) {
|
||||||
|
case ProfileContext::Function:
|
||||||
|
case ProfileContext::FunctionCycle:
|
||||||
|
return i;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallView::doUpdate(int changeType, bool)
|
||||||
|
{
|
||||||
|
// Special case ?
|
||||||
|
if (changeType == selectedItemChanged) {
|
||||||
|
|
||||||
|
if (!_selectedItem) {
|
||||||
|
clearSelection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallItem* ci = (CallItem*) currentItem();
|
||||||
|
TraceCall* c;
|
||||||
|
CostItem* ti;
|
||||||
|
if (ci) {
|
||||||
|
c = ci->call();
|
||||||
|
ti = _showCallers ? c->caller() : c->called();
|
||||||
|
if (ti == _selectedItem) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTreeWidgetItem *item = 0;
|
||||||
|
for (int i=0; i<topLevelItemCount();i++) {
|
||||||
|
item = topLevelItem(i);
|
||||||
|
c = ((CallItem*) item)->call();
|
||||||
|
ti = _showCallers ? c->caller() : c->called();
|
||||||
|
if (ti == _selectedItem) {
|
||||||
|
scrollToItem(item);
|
||||||
|
setCurrentItem(item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!item && ci) clearSelection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeType == groupTypeChanged) {
|
||||||
|
QTreeWidgetItem *item;
|
||||||
|
for (int i=0; i<topLevelItemCount(); i++){
|
||||||
|
item = topLevelItem(i);
|
||||||
|
((CallItem*)item)->updateGroup();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallView::refresh()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
setColumnWidth(1, _eventType2 ? 50:0);
|
||||||
|
|
||||||
|
if (_eventType) {
|
||||||
|
headerItem()->setText(0, _eventType->name());
|
||||||
|
headerItem()->setText(1, tr("%1 per call").arg(_eventType->name()));
|
||||||
|
}
|
||||||
|
if (_eventType2) {
|
||||||
|
headerItem()->setText(2, _eventType2->name());
|
||||||
|
headerItem()->setText(3, tr("%1 per call").arg(_eventType2->name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_data || !_activeItem) return;
|
||||||
|
|
||||||
|
TraceFunction* f = activeFunction();
|
||||||
|
if (!f) return;
|
||||||
|
|
||||||
|
// In the call lists, we skip cycles to show the real call relations
|
||||||
|
TraceCallList l = _showCallers ? f->callers(true) : f->callings(true);
|
||||||
|
|
||||||
|
QList<QTreeWidgetItem*> items;
|
||||||
|
foreach(TraceCall* call, l)
|
||||||
|
if (call->subCost(_eventType)>0)
|
||||||
|
items.append(new CallItem(this, 0, call));
|
||||||
|
|
||||||
|
// when inserting, switch off sorting for performance reason
|
||||||
|
setSortingEnabled(false);
|
||||||
|
addTopLevelItems(items);
|
||||||
|
setSortingEnabled(true);
|
||||||
|
// enabling sorting switches on the indicator, but we want it off
|
||||||
|
header()->setSortIndicatorShown(false);
|
||||||
|
// resize to content now (section size still can be interactively changed)
|
||||||
|
header()->resizeSections(QHeaderView::ResizeToContents);
|
||||||
|
|
||||||
|
if (!_eventType2) {
|
||||||
|
setColumnWidth(2, 0);
|
||||||
|
setColumnWidth(3, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "moc_callview.cpp"
|
59
kcachegrind/libviews/callview.h
Normal file
59
kcachegrind/libviews/callview.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003-2009 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call Views
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CALLVIEW_H
|
||||||
|
#define CALLVIEW_H
|
||||||
|
|
||||||
|
#include <QTreeWidget>
|
||||||
|
#include "tracedata.h"
|
||||||
|
#include "traceitemview.h"
|
||||||
|
|
||||||
|
class CallView: public QTreeWidget, public TraceItemView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CallView(bool showCallers, TraceItemView* parentView,
|
||||||
|
QWidget* parent=0);
|
||||||
|
|
||||||
|
virtual QWidget* widget() { return this; }
|
||||||
|
QString whatsThis() const;
|
||||||
|
bool showCallers() const { return _showCallers; }
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void context(const QPoint &);
|
||||||
|
void selectedSlot(QTreeWidgetItem*, QTreeWidgetItem*);
|
||||||
|
void activatedSlot(QTreeWidgetItem*, int);
|
||||||
|
void headerClicked(int);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent* event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CostItem* canShow(CostItem*);
|
||||||
|
void doUpdate(int, bool);
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
bool _showCallers;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
130
kcachegrind/libviews/costlistitem.cpp
Normal file
130
kcachegrind/libviews/costlistitem.cpp
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2002-2011 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "costlistitem.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
#include "listutils.h"
|
||||||
|
#include "coverage.h"
|
||||||
|
#include "globalguiconfig.h"
|
||||||
|
|
||||||
|
|
||||||
|
// CostListItem
|
||||||
|
|
||||||
|
|
||||||
|
CostListItem::CostListItem(QTreeWidget* parent, TraceCostItem* costItem,
|
||||||
|
EventType* et, int size)
|
||||||
|
:QTreeWidgetItem(parent)
|
||||||
|
{
|
||||||
|
_groupSize = size;
|
||||||
|
_skipped = 0;
|
||||||
|
_costItem = costItem;
|
||||||
|
setEventType(et);
|
||||||
|
|
||||||
|
setTextAlignment(0, Qt::AlignRight);
|
||||||
|
|
||||||
|
if (costItem) {
|
||||||
|
updateName();
|
||||||
|
setIcon(1, colorPixmap(10, 10,
|
||||||
|
GlobalGUIConfig::groupColor(_costItem)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CostListItem::CostListItem(QTreeWidget* parent, int skipped,
|
||||||
|
TraceCostItem* costItem, EventType* et)
|
||||||
|
:QTreeWidgetItem(parent)
|
||||||
|
{
|
||||||
|
_skipped = skipped;
|
||||||
|
_costItem = costItem;
|
||||||
|
setEventType(et);
|
||||||
|
|
||||||
|
setTextAlignment(0, Qt::AlignRight);
|
||||||
|
|
||||||
|
setText(1, QObject::tr("(%n item(s) skipped)", "", _skipped));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CostListItem::setEventType(EventType* et)
|
||||||
|
{
|
||||||
|
_eventType = et;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CostListItem::updateName()
|
||||||
|
{
|
||||||
|
if (!_costItem) return;
|
||||||
|
|
||||||
|
QString n = _costItem->prettyName();
|
||||||
|
if (_groupSize>=0) n += QString(" (%1)").arg(_groupSize);
|
||||||
|
|
||||||
|
setText(1, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CostListItem::setSize(int s)
|
||||||
|
{
|
||||||
|
_groupSize = s;
|
||||||
|
updateName();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CostListItem::update()
|
||||||
|
{
|
||||||
|
if (!_costItem) return;
|
||||||
|
TraceData* d = _costItem->data();
|
||||||
|
|
||||||
|
double total = d->subCost(_eventType);
|
||||||
|
if (total == 0.0) {
|
||||||
|
setText(0, QString("---"));
|
||||||
|
setIcon(0, QPixmap());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pure = _costItem->subCost(_eventType);
|
||||||
|
double pure = 100.0 * _pure / total;
|
||||||
|
QString str;
|
||||||
|
if (GlobalConfig::showPercentage())
|
||||||
|
str = QString("%1").arg(pure, 0, 'f', GlobalConfig::percentPrecision());
|
||||||
|
else
|
||||||
|
str = _costItem->prettySubCost(_eventType);
|
||||||
|
|
||||||
|
if (_skipped) {
|
||||||
|
// special handling for skip entries...
|
||||||
|
setText(0, QString("< %1").arg(str));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setText(0, str);
|
||||||
|
setIcon(0, costPixmap(_eventType, _costItem, total, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CostListItem::operator< ( const QTreeWidgetItem & other ) const
|
||||||
|
{
|
||||||
|
const CostListItem* fi1 = this;
|
||||||
|
const CostListItem* fi2 = (CostListItem*) &other;
|
||||||
|
int col = treeWidget()->sortColumn();
|
||||||
|
|
||||||
|
// a skip entry is always sorted last
|
||||||
|
if (fi1->_skipped) return true;
|
||||||
|
if (fi2->_skipped) return false;
|
||||||
|
|
||||||
|
if (col==0)
|
||||||
|
return (fi1->_pure < fi2->_pure);
|
||||||
|
|
||||||
|
return QTreeWidgetItem::operator <(other);
|
||||||
|
}
|
53
kcachegrind/libviews/costlistitem.h
Normal file
53
kcachegrind/libviews/costlistitem.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COSTLISTITEM_H
|
||||||
|
#define COSTLISTITEM_H
|
||||||
|
|
||||||
|
#include <QTreeWidget>
|
||||||
|
|
||||||
|
#include "tracedata.h"
|
||||||
|
|
||||||
|
class CostListItem: public QTreeWidgetItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CostListItem(QTreeWidget* parent, TraceCostItem* cost,
|
||||||
|
EventType* et, int size = -1);
|
||||||
|
// entry with multiple skipped items
|
||||||
|
CostListItem(QTreeWidget* parent, int skipped, TraceCostItem* cost,
|
||||||
|
EventType* et);
|
||||||
|
|
||||||
|
bool operator< ( const QTreeWidgetItem & other ) const;
|
||||||
|
TraceCostItem* costItem() { return (_skipped) ? 0 : _costItem; }
|
||||||
|
void setEventType(EventType* et);
|
||||||
|
void update();
|
||||||
|
void setSize(int s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateName();
|
||||||
|
|
||||||
|
SubCost _pure;
|
||||||
|
EventType* _eventType;
|
||||||
|
TraceCostItem* _costItem;
|
||||||
|
// >0 only for last item in list, if items are skipped
|
||||||
|
int _skipped;
|
||||||
|
// number of items in group, is put in parenthesis after name
|
||||||
|
int _groupSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
338
kcachegrind/libviews/coverageitem.cpp
Normal file
338
kcachegrind/libviews/coverageitem.cpp
Normal file
|
@ -0,0 +1,338 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Items of coverage view.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "coverageitem.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "globalguiconfig.h"
|
||||||
|
#include "listutils.h"
|
||||||
|
#include "coverage.h"
|
||||||
|
|
||||||
|
|
||||||
|
// CallerCoverageItem
|
||||||
|
|
||||||
|
|
||||||
|
CallerCoverageItem::CallerCoverageItem(QTreeWidget* parent, Coverage* c,
|
||||||
|
TraceFunction* base,
|
||||||
|
EventType* ct,
|
||||||
|
ProfileContext::Type gt)
|
||||||
|
: QTreeWidgetItem(parent)
|
||||||
|
{
|
||||||
|
_skipped = 0;
|
||||||
|
_coverage = c;
|
||||||
|
_function = c->function();
|
||||||
|
_base = base;
|
||||||
|
_groupType = ProfileContext::InvalidType;
|
||||||
|
|
||||||
|
setText(3, _function->prettyNameWithLocation());
|
||||||
|
setTextAlignment(0, Qt::AlignRight);
|
||||||
|
setTextAlignment(1, Qt::AlignRight);
|
||||||
|
setTextAlignment(2, Qt::AlignRight);
|
||||||
|
setCostType(ct);
|
||||||
|
setGroupType(gt);
|
||||||
|
}
|
||||||
|
|
||||||
|
CallerCoverageItem::CallerCoverageItem(QTreeWidget* parent, int skipped, Coverage* c,
|
||||||
|
TraceFunction* base,
|
||||||
|
EventType* ct,
|
||||||
|
ProfileContext::Type gt)
|
||||||
|
: QTreeWidgetItem(parent)
|
||||||
|
{
|
||||||
|
_skipped = skipped;
|
||||||
|
_coverage = c;
|
||||||
|
_function = c->function();
|
||||||
|
_base = base;
|
||||||
|
_groupType = ProfileContext::InvalidType;
|
||||||
|
|
||||||
|
setText(3, QObject::tr("(%n function(s) skipped)", "", _skipped));
|
||||||
|
setTextAlignment(0, Qt::AlignRight);
|
||||||
|
setTextAlignment(1, Qt::AlignRight);
|
||||||
|
setTextAlignment(2, Qt::AlignRight);
|
||||||
|
setCostType(ct);
|
||||||
|
setGroupType(gt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallerCoverageItem::setGroupType(ProfileContext::Type gt)
|
||||||
|
{
|
||||||
|
if (_skipped) return;
|
||||||
|
if (_groupType == gt) return;
|
||||||
|
_groupType = gt;
|
||||||
|
|
||||||
|
QColor c = GlobalGUIConfig::functionColor(_groupType, _function);
|
||||||
|
setIcon(3, colorPixmap(10, 10, c));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallerCoverageItem::setCostType(EventType* ct)
|
||||||
|
{
|
||||||
|
_costType = ct;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallerCoverageItem::update()
|
||||||
|
{
|
||||||
|
if (!_coverage) {
|
||||||
|
setText(0, QString());
|
||||||
|
setText(1, QString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pSum = 100.0 * _coverage->inclusive();
|
||||||
|
SubCost realSum = _base->inclusive()->subCost(_costType);
|
||||||
|
_sum = SubCost(realSum * _coverage->inclusive());
|
||||||
|
QString str;
|
||||||
|
if (GlobalConfig::showPercentage())
|
||||||
|
str = QString("%1").arg(_pSum, 0, 'f', GlobalConfig::percentPrecision());
|
||||||
|
else
|
||||||
|
str = _sum.pretty();
|
||||||
|
|
||||||
|
if (_skipped) {
|
||||||
|
setText(0, QString("< %1").arg(str));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setText(0, str);
|
||||||
|
setIcon(0, partitionPixmap(25, 10, _coverage->inclusiveHistogram(), 0,
|
||||||
|
Coverage::maxHistogramDepth, false));
|
||||||
|
|
||||||
|
// call count
|
||||||
|
_cc = SubCost(_coverage->callCount());
|
||||||
|
setText(2, _cc ? _cc.pretty() : QString("(0)"));
|
||||||
|
|
||||||
|
// distance (min/max/median)
|
||||||
|
_distance = _coverage->inclusiveMedian();
|
||||||
|
QString distString;
|
||||||
|
if (_coverage->minDistance() == _coverage->maxDistance())
|
||||||
|
distString = QString::number(_distance);
|
||||||
|
else
|
||||||
|
distString = QString("%1-%2 (%3)")
|
||||||
|
.arg(_coverage->minDistance())
|
||||||
|
.arg(_coverage->maxDistance())
|
||||||
|
.arg(_distance);
|
||||||
|
setText(1, distString);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CallerCoverageItem::operator<( const QTreeWidgetItem & other ) const
|
||||||
|
{
|
||||||
|
const CallerCoverageItem* ci1 = this;
|
||||||
|
const CallerCoverageItem* ci2 = (CallerCoverageItem*) &other;
|
||||||
|
int col = treeWidget()->sortColumn();
|
||||||
|
|
||||||
|
// a skip entry is always sorted last
|
||||||
|
if (ci1->_skipped) return true;
|
||||||
|
if (ci2->_skipped) return false;
|
||||||
|
|
||||||
|
if (col==0) {
|
||||||
|
if (ci1->_pSum < ci2->_pSum) return true;
|
||||||
|
if (ci1->_pSum > ci2->_pSum) return false;
|
||||||
|
|
||||||
|
// for same percentage (e.g. all 100%), use distance info
|
||||||
|
return ci1->_distance < ci2->_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col==1) {
|
||||||
|
return ci1->_distance < ci2->_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col==2) {
|
||||||
|
return ci1->_cc < ci2->_cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QTreeWidgetItem::operator <(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// CalleeCoverageItem
|
||||||
|
|
||||||
|
CalleeCoverageItem::CalleeCoverageItem(QTreeWidget* parent, Coverage* c,
|
||||||
|
TraceFunction* base,
|
||||||
|
EventType* ct,
|
||||||
|
ProfileContext::Type gt)
|
||||||
|
: QTreeWidgetItem(parent)
|
||||||
|
{
|
||||||
|
_skipped = 0;
|
||||||
|
_coverage = c;
|
||||||
|
_function = c ? c->function() : 0;
|
||||||
|
_base = base;
|
||||||
|
_groupType = ProfileContext::InvalidType;
|
||||||
|
|
||||||
|
if ( _function )
|
||||||
|
setText(4, _function->prettyNameWithLocation());
|
||||||
|
|
||||||
|
setTextAlignment(0, Qt::AlignRight);
|
||||||
|
setTextAlignment(1, Qt::AlignRight);
|
||||||
|
setTextAlignment(2, Qt::AlignRight);
|
||||||
|
setTextAlignment(3, Qt::AlignRight);
|
||||||
|
|
||||||
|
setCostType(ct);
|
||||||
|
setGroupType(gt);
|
||||||
|
}
|
||||||
|
|
||||||
|
CalleeCoverageItem::CalleeCoverageItem(QTreeWidget* parent, int skipped, Coverage* c,
|
||||||
|
TraceFunction* base,
|
||||||
|
EventType* ct,
|
||||||
|
ProfileContext::Type gt)
|
||||||
|
: QTreeWidgetItem(parent)
|
||||||
|
{
|
||||||
|
_skipped = skipped;
|
||||||
|
_coverage = c;
|
||||||
|
_function = c ? c->function() : 0;
|
||||||
|
_base = base;
|
||||||
|
_groupType = ProfileContext::InvalidType;
|
||||||
|
|
||||||
|
setText(4, QObject::tr("(%n function(s) skipped)", "", _skipped));
|
||||||
|
|
||||||
|
setTextAlignment(0, Qt::AlignRight);
|
||||||
|
setTextAlignment(1, Qt::AlignRight);
|
||||||
|
setTextAlignment(2, Qt::AlignRight);
|
||||||
|
setTextAlignment(3, Qt::AlignRight);
|
||||||
|
|
||||||
|
setCostType(ct);
|
||||||
|
setGroupType(gt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CalleeCoverageItem::setGroupType(ProfileContext::Type gt)
|
||||||
|
{
|
||||||
|
if (_skipped) return;
|
||||||
|
if (_groupType == gt) return;
|
||||||
|
_groupType = gt;
|
||||||
|
|
||||||
|
QColor c = GlobalGUIConfig::functionColor(_groupType, _function);
|
||||||
|
setIcon(4, colorPixmap(10, 10, c));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CalleeCoverageItem::setCostType(EventType* ct)
|
||||||
|
{
|
||||||
|
_costType = ct;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CalleeCoverageItem::update()
|
||||||
|
{
|
||||||
|
if (!_coverage) {
|
||||||
|
setText(0, QString());
|
||||||
|
setText(1, QString());
|
||||||
|
setText(2, QString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pSum = 100.0 * _coverage->inclusive();
|
||||||
|
|
||||||
|
// pSum/pSelf are percentages of inclusive cost of base
|
||||||
|
SubCost realSum = _base->inclusive()->subCost(_costType);
|
||||||
|
_sum = SubCost(realSum * _coverage->inclusive());
|
||||||
|
|
||||||
|
|
||||||
|
QString str;
|
||||||
|
if (GlobalConfig::showPercentage())
|
||||||
|
str = QString("%1").arg(_pSum, 0, 'f', GlobalConfig::percentPrecision());
|
||||||
|
else
|
||||||
|
str = _sum.pretty();
|
||||||
|
|
||||||
|
if (_skipped) {
|
||||||
|
str = QString("< %1").arg(str);
|
||||||
|
setText(0, str);
|
||||||
|
setText(1, str);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setText(0, str);
|
||||||
|
|
||||||
|
_pSelf = 100.0 * _coverage->self();
|
||||||
|
_self = SubCost(realSum * _coverage->self());
|
||||||
|
|
||||||
|
if (GlobalConfig::showPercentage()) {
|
||||||
|
setText(1, QString("%1")
|
||||||
|
.arg(_pSelf, 0, 'f', GlobalConfig::percentPrecision()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setText(1, _self.pretty());
|
||||||
|
}
|
||||||
|
|
||||||
|
setIcon(0, partitionPixmap(25, 10, _coverage->inclusiveHistogram(), 0,
|
||||||
|
Coverage::maxHistogramDepth, false));
|
||||||
|
setIcon(1, partitionPixmap(25, 10, _coverage->selfHistogram(), 0,
|
||||||
|
Coverage::maxHistogramDepth, false));
|
||||||
|
|
||||||
|
|
||||||
|
_cc = SubCost(_coverage->callCount());
|
||||||
|
setText(3, _cc ? _cc.pretty() : QString("(0)"));
|
||||||
|
|
||||||
|
// for comparations
|
||||||
|
_distance = _coverage->inclusiveMedian();
|
||||||
|
QString distString;
|
||||||
|
if (_coverage->minDistance() == _coverage->maxDistance())
|
||||||
|
distString = QString::number(_distance);
|
||||||
|
else {
|
||||||
|
int sMed = _coverage->selfMedian();
|
||||||
|
QString med;
|
||||||
|
if (_distance == sMed)
|
||||||
|
med = QString::number(_distance);
|
||||||
|
else
|
||||||
|
med = QString("%1/%2").arg(_distance).arg(sMed);
|
||||||
|
|
||||||
|
distString = QString("%1-%2 (%3)")
|
||||||
|
.arg(_coverage->minDistance())
|
||||||
|
.arg(_coverage->maxDistance())
|
||||||
|
.arg(med);
|
||||||
|
}
|
||||||
|
setText(2, distString);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CalleeCoverageItem::operator<( const QTreeWidgetItem & other ) const
|
||||||
|
{
|
||||||
|
CalleeCoverageItem* ci = (CalleeCoverageItem*) &other;
|
||||||
|
int col = treeWidget()->sortColumn();
|
||||||
|
// a skip entry is always sorted last
|
||||||
|
if (_skipped) return true;
|
||||||
|
if (ci->_skipped) return false;
|
||||||
|
|
||||||
|
if (col==0) {
|
||||||
|
if (_pSum < ci->_pSum) return true;
|
||||||
|
if (_pSum > ci->_pSum) return false;
|
||||||
|
|
||||||
|
// for same percentage (e.g. all 100%), use distance info
|
||||||
|
return _distance < ci->_distance;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col==1) {
|
||||||
|
if (_pSelf < ci->_pSelf) return true;
|
||||||
|
if (_pSelf > ci->_pSelf) return false;
|
||||||
|
|
||||||
|
// for same percentage (e.g. all 100%), use distance info
|
||||||
|
return _distance < ci->_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col==2) {
|
||||||
|
// we want to sort the distance in contra direction to costs
|
||||||
|
return _distance < ci->_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col==3) {
|
||||||
|
return _cc < ci->_cc;
|
||||||
|
}
|
||||||
|
return QTreeWidgetItem::operator <(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
86
kcachegrind/libviews/coverageitem.h
Normal file
86
kcachegrind/libviews/coverageitem.h
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Items of coverage view.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COVERAGEITEM_H
|
||||||
|
#define COVERAGEITEM_H
|
||||||
|
|
||||||
|
#include <QTreeWidget>
|
||||||
|
#include "tracedata.h"
|
||||||
|
|
||||||
|
class Coverage;
|
||||||
|
|
||||||
|
class CallerCoverageItem: public QTreeWidgetItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CallerCoverageItem(QTreeWidget* parent, Coverage* c,
|
||||||
|
TraceFunction* base,
|
||||||
|
EventType* ct, ProfileContext::Type gt);
|
||||||
|
CallerCoverageItem(QTreeWidget* parent, int skipped, Coverage* c,
|
||||||
|
TraceFunction* base,
|
||||||
|
EventType* ct, ProfileContext::Type gt);
|
||||||
|
|
||||||
|
bool operator< ( const QTreeWidgetItem & other ) const;
|
||||||
|
TraceFunction* function() { return (_skipped) ? 0 : _function; }
|
||||||
|
void setCostType(EventType* ct);
|
||||||
|
void setGroupType(ProfileContext::Type);
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
float _pSum;
|
||||||
|
SubCost _sum;
|
||||||
|
EventType* _costType;
|
||||||
|
ProfileContext::Type _groupType;
|
||||||
|
SubCost _cc;
|
||||||
|
int _distance, _skipped;
|
||||||
|
TraceFunction *_function, *_base;
|
||||||
|
Coverage* _coverage;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CalleeCoverageItem: public QTreeWidgetItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CalleeCoverageItem(QTreeWidget* parent, Coverage* c,
|
||||||
|
TraceFunction* base,
|
||||||
|
EventType* ct, ProfileContext::Type gt);
|
||||||
|
CalleeCoverageItem(QTreeWidget* parent, int skipped, Coverage* c,
|
||||||
|
TraceFunction* base,
|
||||||
|
EventType* ct, ProfileContext::Type gt);
|
||||||
|
|
||||||
|
bool operator< ( const QTreeWidgetItem & other ) const;
|
||||||
|
TraceFunction* function() { return (_skipped) ? 0 : _function; }
|
||||||
|
void setCostType(EventType* ct);
|
||||||
|
void setGroupType(ProfileContext::Type);
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
float _pSum, _pSelf;
|
||||||
|
SubCost _sum, _self;
|
||||||
|
EventType* _costType;
|
||||||
|
ProfileContext::Type _groupType;
|
||||||
|
SubCost _cc;
|
||||||
|
int _distance, _skipped;
|
||||||
|
TraceFunction *_function, *_base;
|
||||||
|
Coverage* _coverage;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
369
kcachegrind/libviews/coverageview.cpp
Normal file
369
kcachegrind/libviews/coverageview.cpp
Normal file
|
@ -0,0 +1,369 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Coverage Views
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "coverageview.h"
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
#include "globalconfig.h"
|
||||||
|
#include "coverageitem.h"
|
||||||
|
#include "coverage.h"
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// CoverageView
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
CoverageView::CoverageView(bool showCallers, TraceItemView* parentView, QWidget* parent)
|
||||||
|
: QTreeWidget(parent), TraceItemView(parentView)
|
||||||
|
{
|
||||||
|
_showCallers = showCallers;
|
||||||
|
|
||||||
|
QStringList labels;
|
||||||
|
labels << tr( "Incl." );
|
||||||
|
if (_showCallers) {
|
||||||
|
setColumnCount(4);
|
||||||
|
labels << tr( "Distance" ),
|
||||||
|
labels << tr( "Called" ),
|
||||||
|
labels << tr( "Caller" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setColumnCount(5);
|
||||||
|
labels << tr( "Self" ),
|
||||||
|
labels << tr( "Distance" ),
|
||||||
|
labels << tr( "Calling" ),
|
||||||
|
labels << tr( "Callee" );
|
||||||
|
}
|
||||||
|
setHeaderLabels(labels);
|
||||||
|
|
||||||
|
// forbid scaling icon pixmaps to smaller size
|
||||||
|
setIconSize(QSize(99,99));
|
||||||
|
setAllColumnsShowFocus(true);
|
||||||
|
setRootIsDecorated(false);
|
||||||
|
setUniformRowHeights(true);
|
||||||
|
// sorting will be enabled after refresh()
|
||||||
|
sortByColumn(0, Qt::DescendingOrder);
|
||||||
|
setMinimumHeight(50);
|
||||||
|
|
||||||
|
this->setWhatsThis( whatsThis() );
|
||||||
|
|
||||||
|
connect( this,
|
||||||
|
SIGNAL( currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
|
||||||
|
SLOT( selectedSlot(QTreeWidgetItem*,QTreeWidgetItem*) ) );
|
||||||
|
|
||||||
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect( this,
|
||||||
|
SIGNAL(customContextMenuRequested(const QPoint &) ),
|
||||||
|
SLOT(context(const QPoint &)));
|
||||||
|
|
||||||
|
connect(this,
|
||||||
|
SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
|
||||||
|
SLOT(activatedSlot(QTreeWidgetItem*,int)));
|
||||||
|
|
||||||
|
connect(header(), SIGNAL(sectionClicked(int)),
|
||||||
|
this, SLOT(headerClicked(int)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CoverageView::whatsThis() const
|
||||||
|
{
|
||||||
|
return _showCallers ?
|
||||||
|
tr( "<b>List of all Callers</b>"
|
||||||
|
"<p>This list shows all functions calling the "
|
||||||
|
"current selected one, either directly or with "
|
||||||
|
"several functions in-between on the stack; the "
|
||||||
|
"number of functions in-between plus one "
|
||||||
|
"is called the <em>Distance</em> (e.g. "
|
||||||
|
"for function A,B,C there exists a call from "
|
||||||
|
"A to C when A calls B and B calls C, i.e. "
|
||||||
|
"A => B => C. The distance here is 2).</p>"
|
||||||
|
|
||||||
|
"<p>Absolute cost shown is the cost spent in the "
|
||||||
|
"selected function while a listed function is active; "
|
||||||
|
"relative cost is the percentage of all cost spent in "
|
||||||
|
"the selected function while the listed one is "
|
||||||
|
"active. The cost graphic shows logarithmic "
|
||||||
|
"percentage with a different color for each "
|
||||||
|
"distance.</p>"
|
||||||
|
|
||||||
|
"<p>As there can be many calls from the same function, "
|
||||||
|
"the distance column sometimes shows "
|
||||||
|
"the range of distances for all "
|
||||||
|
"calls happening; then, in parentheses, there is the "
|
||||||
|
"medium distance, i.e. the distance where most of the "
|
||||||
|
"call costs happened.</p>"
|
||||||
|
|
||||||
|
"<p>Selecting a function makes it the current selected "
|
||||||
|
"one of this information panel. "
|
||||||
|
"If there are two panels (Split mode), the "
|
||||||
|
"function of the other panel is changed instead.</p>") :
|
||||||
|
|
||||||
|
tr( "<b>List of all Callees</b>"
|
||||||
|
"<p>This list shows all functions called by the "
|
||||||
|
"current selected one, either directly or with "
|
||||||
|
"several function in-between on the stack; the "
|
||||||
|
"number of function in-between plus one "
|
||||||
|
"is called the <em>Distance</em> (e.g. "
|
||||||
|
"for function A,B,C there exists a call from "
|
||||||
|
"A to C when A calls B and B calls C, i.e. "
|
||||||
|
"A => B => C. The distance here is 2).</p>"
|
||||||
|
|
||||||
|
"<p>Absolute cost shown is the cost spent in the "
|
||||||
|
"listed function while the selected is active; "
|
||||||
|
"relative cost is the percentage of all cost spent in "
|
||||||
|
"the listed function while the selected one is active. "
|
||||||
|
"The cost graphic always shows logarithmic "
|
||||||
|
"percentage with a different color for each "
|
||||||
|
"distance.</p>"
|
||||||
|
|
||||||
|
"<p>As there can be many calls to the same function, "
|
||||||
|
"the distance column sometimes shows "
|
||||||
|
"the range of distances for all "
|
||||||
|
"calls happening; then, in parentheses, there is the "
|
||||||
|
"medium distance, i.e. the distance where most of the "
|
||||||
|
"call costs happened.</p>"
|
||||||
|
|
||||||
|
"<p>Selecting a function makes it the current selected "
|
||||||
|
"one of this information panel. "
|
||||||
|
"If there are two panels (Split mode), the "
|
||||||
|
"function of the other panel is changed instead.</p>");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverageView::context(const QPoint & p)
|
||||||
|
{
|
||||||
|
int c = columnAt(p.x());
|
||||||
|
QTreeWidgetItem* i = itemAt(p);
|
||||||
|
QMenu popup;
|
||||||
|
|
||||||
|
TraceFunction* f = 0;
|
||||||
|
if (i) {
|
||||||
|
f = _showCallers ?
|
||||||
|
((CallerCoverageItem*)i)->function() :
|
||||||
|
((CalleeCoverageItem*)i)->function();
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction* activateFunctionAction = 0;
|
||||||
|
if (f) {
|
||||||
|
QString menuText = tr("Go to '%1'").arg(GlobalConfig::shortenSymbol(f->prettyName()));
|
||||||
|
activateFunctionAction = popup.addAction(menuText);
|
||||||
|
popup.addSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((c == 0) || (!_showCallers && c == 1)) {
|
||||||
|
addEventTypeMenu(&popup, false);
|
||||||
|
popup.addSeparator();
|
||||||
|
}
|
||||||
|
addGoMenu(&popup);
|
||||||
|
|
||||||
|
QAction* a = popup.exec(mapToGlobal(p + QPoint(0,header()->height())));
|
||||||
|
if (a == activateFunctionAction)
|
||||||
|
TraceItemView::activated(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverageView::selectedSlot(QTreeWidgetItem* i, QTreeWidgetItem*)
|
||||||
|
{
|
||||||
|
TraceFunction* f = 0;
|
||||||
|
if (i) {
|
||||||
|
f = _showCallers ?
|
||||||
|
((CallerCoverageItem*)i)->function() :
|
||||||
|
((CalleeCoverageItem*)i)->function();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f) {
|
||||||
|
_selectedItem = f;
|
||||||
|
selected(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverageView::activatedSlot(QTreeWidgetItem* i, int)
|
||||||
|
{
|
||||||
|
TraceFunction* f = 0;
|
||||||
|
if (i) {
|
||||||
|
f = _showCallers ?
|
||||||
|
((CallerCoverageItem*)i)->function() :
|
||||||
|
((CalleeCoverageItem*)i)->function();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f) TraceItemView::activated(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverageView::headerClicked(int col)
|
||||||
|
{
|
||||||
|
// distance and name columns should be sortable in both ways
|
||||||
|
if (_showCallers) {
|
||||||
|
if ((col == 1) || (col==3)) return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((col == 2) || (col==4)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all others only descending
|
||||||
|
sortByColumn(col, Qt::DescendingOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverageView::keyPressEvent(QKeyEvent* event)
|
||||||
|
{
|
||||||
|
QTreeWidgetItem *item = currentItem();
|
||||||
|
if (item && ((event->key() == Qt::Key_Return) ||
|
||||||
|
(event->key() == Qt::Key_Space)))
|
||||||
|
{
|
||||||
|
TraceFunction* f;
|
||||||
|
f = _showCallers ?
|
||||||
|
((CallerCoverageItem*)item)->function() :
|
||||||
|
((CalleeCoverageItem*)item)->function();
|
||||||
|
TraceItemView::activated(f);
|
||||||
|
}
|
||||||
|
QTreeView::keyPressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
CostItem* CoverageView::canShow(CostItem* i)
|
||||||
|
{
|
||||||
|
ProfileContext::Type t = i ? i->type() : ProfileContext::InvalidType;
|
||||||
|
|
||||||
|
switch(t) {
|
||||||
|
case ProfileContext::Function:
|
||||||
|
case ProfileContext::FunctionCycle:
|
||||||
|
return i;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverageView::doUpdate(int changeType, bool)
|
||||||
|
{
|
||||||
|
// Special case ?
|
||||||
|
if (changeType == selectedItemChanged) {
|
||||||
|
|
||||||
|
if (!_selectedItem) {
|
||||||
|
clearSelection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceFunction* f = 0;
|
||||||
|
QTreeWidgetItem* i = currentItem();
|
||||||
|
if (i) {
|
||||||
|
f = _showCallers ?
|
||||||
|
((CallerCoverageItem*)i)->function() :
|
||||||
|
((CalleeCoverageItem*)i)->function();
|
||||||
|
}
|
||||||
|
if (f == _selectedItem) return;
|
||||||
|
|
||||||
|
QTreeWidgetItem *item;
|
||||||
|
for (int i=0; i<topLevelItemCount(); i++) {
|
||||||
|
item = this->topLevelItem(i);
|
||||||
|
f = _showCallers ?
|
||||||
|
((CallerCoverageItem*)item)->function() :
|
||||||
|
((CalleeCoverageItem*)item)->function();
|
||||||
|
if (f == _selectedItem) {
|
||||||
|
scrollToItem(item);
|
||||||
|
setCurrentItem(item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeType == groupTypeChanged) {
|
||||||
|
QTreeWidgetItem *item;
|
||||||
|
for (int i=0; i<topLevelItemCount();i++) {
|
||||||
|
item = topLevelItem(i);
|
||||||
|
if (_showCallers)
|
||||||
|
((CallerCoverageItem*)item)->setGroupType(_groupType);
|
||||||
|
else
|
||||||
|
((CalleeCoverageItem*)item)->setGroupType(_groupType);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverageView::refresh()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
|
||||||
|
if (!_data || !_activeItem) return;
|
||||||
|
|
||||||
|
ProfileContext::Type t = _activeItem->type();
|
||||||
|
TraceFunction* f = 0;
|
||||||
|
if (t == ProfileContext::Function) f = (TraceFunction*) _activeItem;
|
||||||
|
if (t == ProfileContext::FunctionCycle) f = (TraceFunction*) _activeItem;
|
||||||
|
if (!f) return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_hc.clear(GlobalConfig::maxListCount());
|
||||||
|
SubCost realSum = f->inclusive()->subCost(_eventType);
|
||||||
|
|
||||||
|
TraceFunctionList l;
|
||||||
|
if (_showCallers)
|
||||||
|
l = Coverage::coverage(f, Coverage::Caller, _eventType);
|
||||||
|
else
|
||||||
|
l = Coverage::coverage(f, Coverage::Called, _eventType);
|
||||||
|
|
||||||
|
foreach(TraceFunction* f2, l) {
|
||||||
|
Coverage* c = (Coverage*) f2->association(Coverage::Rtti);
|
||||||
|
if (c && (c->inclusive()>0.0))
|
||||||
|
_hc.addCost(f2, SubCost(realSum * c->inclusive()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QTreeWidgetItem*> items;
|
||||||
|
QTreeWidgetItem* item;
|
||||||
|
TraceFunction* ff;
|
||||||
|
for(int i=0;i<_hc.realCount();i++) {
|
||||||
|
ff = (TraceFunction*) _hc[i];
|
||||||
|
Coverage* c = (Coverage*) ff->association(Coverage::Rtti);
|
||||||
|
if (_showCallers)
|
||||||
|
item = new CallerCoverageItem(0, c, f, _eventType, _groupType);
|
||||||
|
else
|
||||||
|
item = new CalleeCoverageItem(0, c, f, _eventType, _groupType);
|
||||||
|
items.append(item);
|
||||||
|
}
|
||||||
|
if (_hc.hasMore()) {
|
||||||
|
// a placeholder for all the functions skipped ...
|
||||||
|
ff = (TraceFunction*) _hc[_hc.maxSize()-1];
|
||||||
|
Coverage* c = (Coverage*) ff->association(Coverage::Rtti);
|
||||||
|
if (_showCallers)
|
||||||
|
item = new CallerCoverageItem(0, _hc.count() - _hc.maxSize(),
|
||||||
|
c, f, _eventType, _groupType);
|
||||||
|
else
|
||||||
|
item = new CalleeCoverageItem(0, _hc.count() - _hc.maxSize(),
|
||||||
|
c, f, _eventType, _groupType);
|
||||||
|
items.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// when inserting, switch off sorting for performance reason
|
||||||
|
setSortingEnabled(false);
|
||||||
|
addTopLevelItems(items);
|
||||||
|
setSortingEnabled(true);
|
||||||
|
// enabling sorting switches on the indicator, but we want it off
|
||||||
|
header()->setSortIndicatorShown(false);
|
||||||
|
// resize to content now (section size still can be interactively changed)
|
||||||
|
header()->resizeSections(QHeaderView::ResizeToContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "moc_coverageview.cpp"
|
61
kcachegrind/libviews/coverageview.h
Normal file
61
kcachegrind/libviews/coverageview.h
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003-2009 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Coverage Views
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COVERAGEVIEW_H
|
||||||
|
#define COVERAGEVIEW_H
|
||||||
|
|
||||||
|
#include <QTreeWidget>
|
||||||
|
|
||||||
|
#include "tracedata.h"
|
||||||
|
#include "traceitemview.h"
|
||||||
|
#include "listutils.h"
|
||||||
|
|
||||||
|
class CoverageView: public QTreeWidget, public TraceItemView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CoverageView(bool showCallers, TraceItemView* parentView,
|
||||||
|
QWidget* parent = 0);
|
||||||
|
|
||||||
|
virtual QWidget* widget() { return this; }
|
||||||
|
QString whatsThis() const;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void context(const QPoint &);
|
||||||
|
void selectedSlot(QTreeWidgetItem*, QTreeWidgetItem*);
|
||||||
|
void activatedSlot(QTreeWidgetItem*, int);
|
||||||
|
void headerClicked(int);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent* event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CostItem* canShow(CostItem*);
|
||||||
|
void doUpdate(int, bool);
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
HighestCostList _hc;
|
||||||
|
bool _showCallers;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
156
kcachegrind/libviews/eventtypeitem.cpp
Normal file
156
kcachegrind/libviews/eventtypeitem.cpp
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Items of event type view.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "eventtypeitem.h"
|
||||||
|
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
#include "globalconfig.h"
|
||||||
|
#include "listutils.h"
|
||||||
|
|
||||||
|
|
||||||
|
// EventTypeItem
|
||||||
|
|
||||||
|
|
||||||
|
EventTypeItem::EventTypeItem(TraceCostItem* costItem,
|
||||||
|
EventType* ct, ProfileContext::Type gt)
|
||||||
|
{
|
||||||
|
_costItem = costItem;
|
||||||
|
_eventType = ct;
|
||||||
|
_groupType = gt;
|
||||||
|
|
||||||
|
setTextAlignment(1, Qt::AlignRight);
|
||||||
|
setTextAlignment(2, Qt::AlignRight);
|
||||||
|
setTextAlignment(3, Qt::AlignRight);
|
||||||
|
setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
|
if (ct) {
|
||||||
|
setText(0, ct->longName());
|
||||||
|
setText(3, ct->name());
|
||||||
|
setText(5, ct->parsedFormula());
|
||||||
|
QString formula = ct->formula();
|
||||||
|
if (!ct->isReal()) {
|
||||||
|
setText(4, "=");
|
||||||
|
// we have a virtual type: allow editing
|
||||||
|
// FIXME: How to enable this only for columns 0,3,5 ?!
|
||||||
|
setFlags(flags() | Qt::ItemIsEditable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setText(0, QObject::tr("Unknown Type"));
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventTypeItem::setGroupType(ProfileContext::Type gt)
|
||||||
|
{
|
||||||
|
if (_groupType == gt) return;
|
||||||
|
|
||||||
|
_groupType = gt;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventTypeItem::update()
|
||||||
|
{
|
||||||
|
TraceData* d = _costItem ? _costItem->data() : 0;
|
||||||
|
double total = d ? ((double)d->subCost(_eventType)) : 0.0;
|
||||||
|
|
||||||
|
if (total == 0.0) {
|
||||||
|
setText(1, "-");
|
||||||
|
setIcon(1, QIcon());
|
||||||
|
setText(2, "-");
|
||||||
|
setIcon(2, QIcon());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceFunction* f = (_costItem && _costItem->type()==ProfileContext::Function) ?
|
||||||
|
(TraceFunction*)_costItem : 0;
|
||||||
|
|
||||||
|
ProfileCostArray* selfTotalCost = f ? f->data() : d;
|
||||||
|
if (f && GlobalConfig::showExpanded()) {
|
||||||
|
ProfileCostArray* parent = 0;
|
||||||
|
switch(_groupType) {
|
||||||
|
case ProfileContext::Object: parent = f->object(); break;
|
||||||
|
case ProfileContext::Class: parent = f->cls(); break;
|
||||||
|
case ProfileContext::File: parent = f->file(); break;
|
||||||
|
case ProfileContext::FunctionCycle: parent = f->cycle(); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
if (parent) selfTotalCost = parent;
|
||||||
|
}
|
||||||
|
if (_costItem && _costItem->type()==ProfileContext::FunctionCycle) {
|
||||||
|
f = (TraceFunction*)_costItem;
|
||||||
|
selfTotalCost = f->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
double selfTotal = selfTotalCost->subCost(_eventType);
|
||||||
|
|
||||||
|
// for all cost items there is a self cost
|
||||||
|
_pure = _costItem ? _costItem->subCost(_eventType) : SubCost(0);
|
||||||
|
double pure = 100.0 * _pure / selfTotal;
|
||||||
|
if (GlobalConfig::showPercentage()) {
|
||||||
|
setText(2, QString("%1")
|
||||||
|
.arg(pure, 0, 'f', GlobalConfig::percentPrecision()));
|
||||||
|
}
|
||||||
|
else if (_costItem)
|
||||||
|
setText(2, _costItem->prettySubCost(_eventType));
|
||||||
|
|
||||||
|
setIcon(2, QIcon(costPixmap(_eventType, _costItem, selfTotal, false)));
|
||||||
|
|
||||||
|
if (!f) {
|
||||||
|
setText(1, "-");
|
||||||
|
setIcon(1, QIcon());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sum = f->inclusive()->subCost(_eventType);
|
||||||
|
double sum = 100.0 * _sum / total;
|
||||||
|
if (GlobalConfig::showPercentage()) {
|
||||||
|
setText(1, QString("%1")
|
||||||
|
.arg(sum, 0, 'f', GlobalConfig::percentPrecision()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
setText(1, _sum.pretty());
|
||||||
|
|
||||||
|
setIcon(1, QIcon(costPixmap(_eventType, f->inclusive(), total, false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventTypeItem::operator<(const QTreeWidgetItem &other) const
|
||||||
|
{
|
||||||
|
int col = treeWidget()->sortColumn();
|
||||||
|
EventTypeItem* o = (EventTypeItem*) &other;
|
||||||
|
if (col==0)
|
||||||
|
return _sum < o->_sum;
|
||||||
|
if (col==1)
|
||||||
|
return _pure < o->_pure;
|
||||||
|
|
||||||
|
return QTreeWidgetItem::operator<(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant EventTypeItem::data(int column, int role) const
|
||||||
|
{
|
||||||
|
if ((column == 5) && (role == Qt::EditRole))
|
||||||
|
return QVariant(_eventType->formula());
|
||||||
|
return QTreeWidgetItem::data(column, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
51
kcachegrind/libviews/eventtypeitem.h
Normal file
51
kcachegrind/libviews/eventtypeitem.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Items of event type view.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EVENTTYPEITEM_H
|
||||||
|
#define EVENTTYPEITEM_H
|
||||||
|
|
||||||
|
#include <QTreeWidgetItem>
|
||||||
|
|
||||||
|
#include "tracedata.h"
|
||||||
|
|
||||||
|
class EventTypeItem: public QTreeWidgetItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EventTypeItem(TraceCostItem* costItem,
|
||||||
|
EventType* ct, ProfileContext::Type gt);
|
||||||
|
|
||||||
|
bool operator<(const QTreeWidgetItem &other) const;
|
||||||
|
void setGroupType(ProfileContext::Type);
|
||||||
|
TraceCostItem* costItem() { return _costItem; }
|
||||||
|
EventType* eventType() { return _eventType; }
|
||||||
|
void update();
|
||||||
|
|
||||||
|
QVariant data(int column, int role) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SubCost _sum, _pure;
|
||||||
|
EventType* _eventType;
|
||||||
|
TraceCostItem* _costItem;
|
||||||
|
ProfileContext::Type _groupType;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EVENTTYPEITEM_H
|
338
kcachegrind/libviews/eventtypeview.cpp
Normal file
338
kcachegrind/libviews/eventtypeview.cpp
Normal file
|
@ -0,0 +1,338 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Event Type View
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "eventtypeview.h"
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QHeaderView>
|
||||||
|
|
||||||
|
#include "eventtypeitem.h"
|
||||||
|
#include "toplevelbase.h"
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// EventTypeView
|
||||||
|
//
|
||||||
|
|
||||||
|
EventTypeView::EventTypeView(TraceItemView* parentView,
|
||||||
|
QWidget* parent, const char* name)
|
||||||
|
: QTreeWidget(parent), TraceItemView(parentView)
|
||||||
|
{
|
||||||
|
setObjectName(name);
|
||||||
|
// forbid scaling icon pixmaps to smaller size
|
||||||
|
setIconSize(QSize(99,99));
|
||||||
|
setColumnCount(6);
|
||||||
|
QStringList labels;
|
||||||
|
labels << tr( "Event Type" )
|
||||||
|
<< tr( "Incl." )
|
||||||
|
<< tr( "Self" )
|
||||||
|
<< tr( "Short" )
|
||||||
|
<< QString()
|
||||||
|
<< tr( "Formula" );
|
||||||
|
setHeaderLabels(labels);
|
||||||
|
// reduce minimum width for '=' column
|
||||||
|
header()->setMinimumSectionSize(10);
|
||||||
|
|
||||||
|
setRootIsDecorated(false);
|
||||||
|
setSortingEnabled(false);
|
||||||
|
setAllColumnsShowFocus(true);
|
||||||
|
setMinimumHeight(50);
|
||||||
|
|
||||||
|
|
||||||
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect( this,
|
||||||
|
SIGNAL(customContextMenuRequested(const QPoint &)),
|
||||||
|
SLOT(context(const QPoint &)));
|
||||||
|
|
||||||
|
// FIXME: Endless jumping among 2 types possible!
|
||||||
|
connect( this,
|
||||||
|
SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
|
||||||
|
SLOT(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)) );
|
||||||
|
|
||||||
|
connect(this,
|
||||||
|
SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
|
||||||
|
SLOT(itemDoubleClicked(QTreeWidgetItem*,int)));
|
||||||
|
|
||||||
|
connect(this,
|
||||||
|
SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||||
|
SLOT(itemChanged(QTreeWidgetItem*,int)));
|
||||||
|
|
||||||
|
setWhatsThis( whatsThis() );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EventTypeView::whatsThis() const
|
||||||
|
{
|
||||||
|
return tr( "<b>Cost Types List</b>"
|
||||||
|
"<p>This list shows all cost types available "
|
||||||
|
"and what the self/inclusive cost of the "
|
||||||
|
"current selected function is for that cost type.</p>"
|
||||||
|
"<p>By choosing a cost type from the list, "
|
||||||
|
"you change the cost type of costs shown "
|
||||||
|
"all over KCachegrind to be the selected one.</p>");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EventTypeView::context(const QPoint & p)
|
||||||
|
{
|
||||||
|
QMenu popup;
|
||||||
|
|
||||||
|
QTreeWidgetItem* i = itemAt(p);
|
||||||
|
EventType* ct = i ? ((EventTypeItem*) i)->eventType() : 0;
|
||||||
|
|
||||||
|
QAction* selectType2Action = 0;
|
||||||
|
QAction* hideType2Action = 0;
|
||||||
|
if (ct)
|
||||||
|
selectType2Action = popup.addAction(tr("Set as Secondary Event Type"));
|
||||||
|
if (_eventType2)
|
||||||
|
hideType2Action = popup.addAction(tr("Hide Secondary Event Type"));
|
||||||
|
if (!popup.isEmpty())
|
||||||
|
popup.addSeparator();
|
||||||
|
|
||||||
|
QAction* editLongNameAction = 0;
|
||||||
|
QAction* editShortNameAction = 0;
|
||||||
|
QAction* editFormulaAction = 0;
|
||||||
|
QAction* removeTypeAction = 0;
|
||||||
|
if (ct && !ct->isReal()) {
|
||||||
|
editLongNameAction = popup.addAction(tr("Edit Long Name"));
|
||||||
|
editShortNameAction = popup.addAction(tr("Edit Short Name"));
|
||||||
|
editFormulaAction = popup.addAction(tr("Edit Formula"));
|
||||||
|
removeTypeAction = popup.addAction(tr("Remove"));
|
||||||
|
popup.addSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
addGoMenu(&popup);
|
||||||
|
|
||||||
|
QAction* newTypeAction = 0;
|
||||||
|
if( _data) {
|
||||||
|
popup.addSeparator();
|
||||||
|
newTypeAction = popup.addAction(tr("New Event Type..."));
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction* a = popup.exec(viewport()->mapToGlobal(p));
|
||||||
|
if (a == hideType2Action) selectedEventType2(0);
|
||||||
|
else if (a == selectType2Action) selectedEventType2(ct);
|
||||||
|
else if (a == editLongNameAction) editItem(i, 0);
|
||||||
|
else if (a == editShortNameAction) editItem(i, 3);
|
||||||
|
else if (a == editFormulaAction) editItem(i, 5);
|
||||||
|
else if (a == removeTypeAction) {
|
||||||
|
|
||||||
|
// search for a previous type
|
||||||
|
EventType* prev = 0, *ct = 0;
|
||||||
|
EventTypeSet* m = _data->eventTypes();
|
||||||
|
for (int i=0;i<m->realCount();i++) {
|
||||||
|
ct = m->realType(i);
|
||||||
|
if (ct) prev = ct;
|
||||||
|
}
|
||||||
|
for (int i=0;i<m->derivedCount();i++) {
|
||||||
|
ct = m->derivedType(i);
|
||||||
|
if (ct == _eventType) break;
|
||||||
|
if (ct) prev = ct;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_data->eventTypes()->remove(ct)) {
|
||||||
|
// select previous cost type
|
||||||
|
selectedEventType(prev);
|
||||||
|
if (_eventType2 == ct)
|
||||||
|
selectedEventType2(prev);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (a == newTypeAction) {
|
||||||
|
int i = 1;
|
||||||
|
while(1) {
|
||||||
|
if (!EventType::knownDerivedType(tr("New%1").arg(i)))
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// add same new cost type to this set and to known types
|
||||||
|
QString shortName = tr("New%1").arg(i);
|
||||||
|
QString longName = tr("New Event Type %1").arg(i);
|
||||||
|
EventType* et;
|
||||||
|
et = new EventType(shortName, longName);
|
||||||
|
et->setFormula(QString()); // event is derived
|
||||||
|
EventType::add(et);
|
||||||
|
// EventType::add() took ownership, need new object
|
||||||
|
et = new EventType(shortName, longName);
|
||||||
|
et->setFormula(QString()); // event is derived
|
||||||
|
_data->eventTypes()->add(et);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EventTypeView::currentItemChanged(QTreeWidgetItem* i, QTreeWidgetItem*)
|
||||||
|
{
|
||||||
|
EventType* ct = i ? ((EventTypeItem*) i)->eventType() : 0;
|
||||||
|
if (ct)
|
||||||
|
selectedEventType(ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventTypeView::itemDoubleClicked(QTreeWidgetItem* i, int)
|
||||||
|
{
|
||||||
|
EventType* ct = i ? ((EventTypeItem*) i)->eventType() : 0;
|
||||||
|
if (ct)
|
||||||
|
selectedEventType2(ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
CostItem* EventTypeView::canShow(CostItem* i)
|
||||||
|
{
|
||||||
|
if (!i) return 0;
|
||||||
|
|
||||||
|
switch(i->type()) {
|
||||||
|
case ProfileContext::Object:
|
||||||
|
case ProfileContext::Class:
|
||||||
|
case ProfileContext::File:
|
||||||
|
case ProfileContext::Call:
|
||||||
|
case ProfileContext::FunctionCycle:
|
||||||
|
case ProfileContext::Function:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventTypeView::doUpdate(int changeType, bool)
|
||||||
|
{
|
||||||
|
// Special case ?
|
||||||
|
if (changeType == selectedItemChanged) return;
|
||||||
|
|
||||||
|
if (changeType == eventType2Changed) return;
|
||||||
|
|
||||||
|
if (changeType == groupTypeChanged) {
|
||||||
|
for(int i = 0; i < topLevelItemCount(); i++)
|
||||||
|
((EventTypeItem*)topLevelItem(i))->setGroupType(_groupType);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeType == eventTypeChanged) {
|
||||||
|
for(int i = 0; i < topLevelItemCount(); i++) {
|
||||||
|
EventTypeItem* item = (EventTypeItem*)topLevelItem(i);
|
||||||
|
if ( item->eventType() == _eventType) {
|
||||||
|
setCurrentItem(item);
|
||||||
|
scrollToItem(item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeType == partsChanged) {
|
||||||
|
for(int i = 0; i < topLevelItemCount(); i++)
|
||||||
|
((EventTypeItem*)topLevelItem(i))->update();
|
||||||
|
|
||||||
|
resizeColumnToContents(1);
|
||||||
|
resizeColumnToContents(2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventTypeView::refresh()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
setColumnWidth(1, 50);
|
||||||
|
setColumnWidth(2, 50);
|
||||||
|
|
||||||
|
if (!_data || !_activeItem) return;
|
||||||
|
switch(_activeItem->type()) {
|
||||||
|
case ProfileContext::Object:
|
||||||
|
case ProfileContext::Class:
|
||||||
|
case ProfileContext::File:
|
||||||
|
case ProfileContext::FunctionCycle:
|
||||||
|
case ProfileContext::Function:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TraceCostItem* c = (TraceCostItem*) _activeItem;
|
||||||
|
|
||||||
|
EventType* ct =0;
|
||||||
|
QTreeWidgetItem* item = 0;
|
||||||
|
QTreeWidgetItem* selected = 0;
|
||||||
|
QList<QTreeWidgetItem*> items;
|
||||||
|
QString sumStr, pureStr;
|
||||||
|
|
||||||
|
EventTypeSet* m = _data->eventTypes();
|
||||||
|
for (int i=0; i<m->realCount();i++) {
|
||||||
|
ct = m->realType(i);
|
||||||
|
item = new EventTypeItem(c, ct, _groupType);
|
||||||
|
if (ct == _eventType) selected = item;
|
||||||
|
items.append(item);
|
||||||
|
}
|
||||||
|
for (int i=0; i<m->derivedCount();i++) {
|
||||||
|
ct = m->derivedType(i);
|
||||||
|
if (!ct) continue;
|
||||||
|
item = new EventTypeItem(c, ct, _groupType);
|
||||||
|
if (ct == _eventType) selected = item;
|
||||||
|
items.append(item);
|
||||||
|
}
|
||||||
|
insertTopLevelItems(0,items);
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
setCurrentItem(selected);
|
||||||
|
scrollToItem(selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int c = 0; c<6; c++)
|
||||||
|
resizeColumnToContents(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventTypeView::itemChanged(QTreeWidgetItem* item, int c)
|
||||||
|
{
|
||||||
|
EventType* ct = item ? ((EventTypeItem*) item)->eventType() : 0;
|
||||||
|
if (!ct || ct->isReal()) return;
|
||||||
|
|
||||||
|
// search for matching known Type
|
||||||
|
int knownCount = EventType::knownTypeCount();
|
||||||
|
EventType* known = 0;
|
||||||
|
for (int i=0; i<knownCount; i++) {
|
||||||
|
known = EventType::knownType(i);
|
||||||
|
if (known->name() == ct->name()) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString t = item->text(c);
|
||||||
|
if (c == 0) {
|
||||||
|
ct->setLongName(t);
|
||||||
|
if (known) known->setLongName(t);
|
||||||
|
}
|
||||||
|
else if (c == 3) {
|
||||||
|
ct->setName(t);
|
||||||
|
if (known) known->setName(t);
|
||||||
|
}
|
||||||
|
else if (c == 5) {
|
||||||
|
ct->setFormula(t);
|
||||||
|
if (known) known->setFormula(t);
|
||||||
|
}
|
||||||
|
else return;
|
||||||
|
|
||||||
|
if (_topLevel) _topLevel->configChanged();
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "moc_eventtypeview.cpp"
|
54
kcachegrind/libviews/eventtypeview.h
Normal file
54
kcachegrind/libviews/eventtypeview.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/* This file is part of KCachegrind.
|
||||||
|
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
||||||
|
|
||||||
|
KCachegrind is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; see the file COPYING. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Event Type View
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EVENTTYPEVIEW_H
|
||||||
|
#define EVENTTYPEVIEW_H
|
||||||
|
|
||||||
|
#include <QTreeWidget>
|
||||||
|
|
||||||
|
#include "tracedata.h"
|
||||||
|
#include "traceitemview.h"
|
||||||
|
|
||||||
|
class EventTypeView: public QTreeWidget, public TraceItemView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit EventTypeView(TraceItemView* parentView,
|
||||||
|
QWidget* parent = 0, const char* name = 0);
|
||||||
|
|
||||||
|
virtual QWidget* widget() { return this; }
|
||||||
|
QString whatsThis() const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void context(const QPoint&);
|
||||||
|
void currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*);
|
||||||
|
void itemDoubleClicked(QTreeWidgetItem*, int);
|
||||||
|
void itemChanged(QTreeWidgetItem*, int);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CostItem* canShow(CostItem*);
|
||||||
|
void doUpdate(int, bool);
|
||||||
|
void refresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue