github.com/jdgcs/sqlite3@v1.12.1-0.20210908114423-bc5f96e4dd51/testdata/tcl/malloctraceviewer.tcl (about) 1 2 package require sqlite3 3 package require Tk 4 5 ############################################################################# 6 # Code to set up scrollbars for widgets. This is generic, boring stuff. 7 # 8 namespace eval autoscroll { 9 proc scrollable {widget path args} { 10 ::ttk::frame $path 11 set w [$widget ${path}.widget {*}$args] 12 set vs [::ttk::scrollbar ${path}.vs] 13 set hs [::ttk::scrollbar ${path}.hs -orient horizontal] 14 grid $w -row 0 -column 0 -sticky nsew 15 16 grid rowconfigure $path 0 -weight 1 17 grid columnconfigure $path 0 -weight 1 18 19 set grid [list grid $vs -row 0 -column 1 -sticky nsew] 20 $w configure -yscrollcommand [list ::autoscroll::scrollcommand $grid $vs] 21 $vs configure -command [list $w yview] 22 set grid [list grid $hs -row 1 -column 0 -sticky nsew] 23 $w configure -xscrollcommand [list ::autoscroll::scrollcommand $grid $hs] 24 $hs configure -command [list $w xview] 25 26 return $w 27 } 28 proc scrollcommand {grid sb args} { 29 $sb set {*}$args 30 set isRequired [expr {[lindex $args 0] != 0.0 || [lindex $args 1] != 1.0}] 31 if {$isRequired && ![winfo ismapped $sb]} { 32 {*}$grid 33 } 34 if {!$isRequired && [winfo ismapped $sb]} { 35 grid forget $sb 36 } 37 } 38 namespace export scrollable 39 } 40 namespace import ::autoscroll::* 41 ############################################################################# 42 43 proc populate_text_widget {db} { 44 $::O(text) configure -state normal 45 set id [lindex [$::O(tree) selection] 0] 46 set frame [lindex $id end] 47 48 set line [$db one {SELECT line FROM frame WHERE frame = $frame}] 49 if {$line ne ""} { 50 foreach {file line} [split $line :] {} 51 set content [$db one "SELECT content FROM file WHERE name = '$file'"] 52 $::O(text) delete 0.0 end 53 54 set iLine 1 55 foreach L [split $content "\n"] { 56 if {$iLine == $line} { 57 $::O(text) insert end "$L\n" highlight 58 } else { 59 $::O(text) insert end "$L\n" 60 } 61 incr iLine 62 } 63 $::O(text) yview -pickplace ${line}.0 64 } 65 $::O(text) configure -state disabled 66 } 67 68 proc populate_index {db} { 69 $::O(text) configure -state normal 70 71 $::O(text) delete 0.0 end 72 $::O(text) insert end "\n\n" 73 74 set L [format " % -40s%12s%12s\n" "Test Case" "Allocations" "Bytes"] 75 $::O(text) insert end $L 76 $::O(text) insert end " [string repeat - 64]\n" 77 78 $db eval { 79 SELECT 'TOTAL' AS ztest, sum(ncall) AS calls, sum(nbyte) AS bytes 80 FROM malloc 81 UNION ALL 82 SELECT ztest AS ztest, sum(ncall) AS calls, sum(nbyte) AS bytes 83 FROM malloc 84 GROUP BY ztest 85 86 ORDER BY 3 DESC 87 } { 88 set tags [list $ztest] 89 if {$ztest eq $::O(current)} { 90 lappend tags highlight 91 } 92 set L [format " % -40s%12s%12s\n" $ztest $calls $bytes] 93 $::O(text) insert end $L $tags 94 95 $::O(text) tag bind $ztest <1> [list populate_tree_widget $db $ztest] 96 $::O(text) tag bind $ztest <Enter> [list $::O(text) configure -cursor hand2] 97 $::O(text) tag bind $ztest <Leave> [list $::O(text) configure -cursor ""] 98 } 99 100 $::O(text) configure -state disabled 101 } 102 103 proc sort_tree_compare {iLeft iRight} { 104 global O 105 switch -- [expr (int($O(tree_sort)/2))] { 106 0 { 107 set left [$O(tree) item $iLeft -text] 108 set right [$O(tree) item $iRight -text] 109 set res [string compare $left $right] 110 } 111 1 { 112 set left [lindex [$O(tree) item $iLeft -values] 0] 113 set right [lindex [$O(tree) item $iRight -values] 0] 114 set res [expr $left - $right] 115 } 116 2 { 117 set left [lindex [$O(tree) item $iLeft -values] 1] 118 set right [lindex [$O(tree) item $iRight -values] 1] 119 set res [expr $left - $right] 120 } 121 } 122 if {$O(tree_sort)&0x01} { 123 set res [expr -1 * $res] 124 } 125 return $res 126 } 127 128 proc sort_tree {iMode} { 129 global O 130 if {$O(tree_sort) == $iMode} { 131 incr O(tree_sort) 132 } else { 133 set O(tree_sort) $iMode 134 } 135 set T $O(tree) 136 set items [$T children {}] 137 set items [lsort -command sort_tree_compare $items] 138 for {set ii 0} {$ii < [llength $items]} {incr ii} { 139 $T move [lindex $items $ii] {} $ii 140 } 141 } 142 143 proc trim_frames {stack} { 144 while {[info exists ::O(ignore.[lindex $stack 0])]} { 145 set stack [lrange $stack 1 end] 146 } 147 return $stack 148 } 149 150 proc populate_tree_widget {db zTest} { 151 $::O(tree) delete [$::O(tree) children {}] 152 153 for {set ii 0} {$ii < 15} {incr ii} { 154 $db eval { 155 SELECT 156 sum(ncall) AS calls, 157 sum(nbyte) AS bytes, 158 trim_frames(lrange(lstack, 0, $ii)) AS stack 159 FROM malloc 160 WHERE (zTest = $zTest OR $zTest = 'TOTAL') AND llength(lstack)>$ii 161 GROUP BY stack 162 HAVING stack != '' 163 } { 164 set parent_id [lrange $stack 0 end-1] 165 set frame [lindex $stack end] 166 set line [$db one {SELECT line FROM frame WHERE frame = $frame}] 167 set line [lindex [split $line /] end] 168 set v [list $calls $bytes] 169 170 catch { 171 $::O(tree) insert $parent_id end -id $stack -text $line -values $v 172 } 173 } 174 } 175 176 set ::O(current) $zTest 177 populate_index $db 178 } 179 180 181 182 set O(tree_sort) 0 183 184 ::ttk::panedwindow .pan -orient horizontal 185 set O(tree) [scrollable ::ttk::treeview .pan.tree] 186 187 frame .pan.right 188 set O(text) [scrollable text .pan.right.text] 189 button .pan.right.index -command {populate_index mddb} -text "Show Index" 190 pack .pan.right.index -side top -fill x 191 pack .pan.right.text -fill both -expand true 192 193 $O(text) tag configure highlight -background wheat 194 $O(text) configure -wrap none -height 35 195 196 .pan add .pan.tree 197 .pan add .pan.right 198 199 $O(tree) configure -columns {calls bytes} 200 $O(tree) heading #0 -text Line -anchor w -command {sort_tree 0} 201 $O(tree) heading calls -text Calls -anchor w -command {sort_tree 2} 202 $O(tree) heading bytes -text Bytes -anchor w -command {sort_tree 4} 203 $O(tree) column #0 -width 150 204 $O(tree) column calls -width 100 205 $O(tree) column bytes -width 100 206 207 pack .pan -fill both -expand 1 208 209 #-------------------------------------------------------------------- 210 # Open the database containing the malloc data. The user specifies the 211 # database to use by passing the file-name on the command line. 212 # 213 proc open_database {} { 214 if {[info exists ::BUILTIN]} { 215 sqlite3 mddb :memory: 216 mddb eval $::BUILTIN 217 wm title . $::argv0 218 } else { 219 set zFilename [lindex $::argv 0] 220 if {$zFilename eq ""} { 221 set zFilename mallocs.sql 222 } 223 set fd [open $zFilename] 224 set zHdr [read $fd 15] 225 if {$zHdr eq "SQLite format 3"} { 226 close $fd 227 sqlite3 mddb $zFilename 228 } else { 229 seek $fd 0 230 sqlite3 mddb :memory: 231 mddb eval [read $fd] 232 close $fd 233 } 234 wm title . $zFilename 235 } 236 237 mddb function lrange -argcount 3 lrange 238 mddb function llength -argcount 1 llength 239 mddb function trim_frames -argcount 1 trim_frames 240 241 mddb eval { 242 SELECT frame FROM frame 243 WHERE line LIKE '%malloc.c:%' OR line LIKE '%mem2.c:%' 244 } { 245 set ::O(ignore.$frame) 1 246 } 247 } 248 249 open_database 250 bind $O(tree) <<TreeviewSelect>> [list populate_text_widget mddb] 251 252 populate_tree_widget mddb [mddb one {SELECT zTest FROM malloc LIMIT 1}] 253