import kcachegrind with automoc migrated

This commit is contained in:
Ivailo Monev 2015-03-04 07:38:41 +00:00
parent 30797fa9c7
commit c8a5bdd328
174 changed files with 46501 additions and 1 deletions

View file

@ -28,3 +28,4 @@ macro_optional_add_subdirectory (kfilereplace)
macro_optional_add_subdirectory (khelpcenter)
macro_optional_add_subdirectory (kaffeine)
macro_optional_add_subdirectory (kamoso)
macro_optional_add_subdirectory (kcachegrind)

1
kcachegrind/AUTHORS Normal file
View file

@ -0,0 +1 @@
Josef Weidendorfer <Josef.Weidendorfer@gmx.de>

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

108
kcachegrind/README Normal file
View 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
View 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

View 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} )

View 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).

View 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
View 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;
}
}
}
}

View 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} )

View 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

View 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 :

View 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)

View 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";
}
}

View 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;
}

View 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);
}
?>

View 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

View 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

View 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} )

View 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 =

View 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"

View 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

View 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>

View 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;
}

View 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

View 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"

View 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

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View 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;

View file

@ -0,0 +1,57 @@
<!DOCTYPE kpartgui>
<kpartgui name="kcachegrind" version="4">
<MenuBar>
<Menu name="file"><text>&amp;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>&amp;View</text>
<Action name="view_cost_type"/>
<Action name="view_cost_type2"/>
<Action name="view_group_type"/>
<Separator/>
<Menu name="layouts"><text>&amp;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>

View 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);
}

View 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

View 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;
}

View 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>

File diff suppressed because it is too large Load diff

View 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
View 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

View 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})

View 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);
}

View 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

File diff suppressed because it is too large Load diff

View 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();
}

View 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

View 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;
}

View 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

View 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();
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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]);
}

View 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

View 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);
}

View 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

View 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

View 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);
}

View 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

View 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);
}

View 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

View 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
View 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

View 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);
}

View 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

View 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;
}

View 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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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
View 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

View 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)

View 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

File diff suppressed because it is too large Load diff

View 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

View 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);
}

View 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

View 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"

View 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

View 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"

View 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

View 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);
}

View 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

View 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);
}

View 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

View 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"

View 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

View 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);
}

View 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

View 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"

View 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