summaryrefslogtreecommitdiff
path: root/gdb/testsuite/lib/cache.exp
blob: dc7fd7119f041df2ec6cf0515ad657fcc6e897ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# Copyright 2012-2022 Free Software Foundation, Inc.

# 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 3 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, see <http://www.gnu.org/licenses/>.


# The in-memory cache.
array set gdb_data_cache {}

# Print pass message msg into gdb.log
proc ignore_pass { msg } {
    verbose -log "gdb_do_cache_wrap ignoring pass: $msg"
}

# Call proc real_name and return the result, while ignoring calls to pass.
proc gdb_do_cache_wrap {real_name} {
    if { [info procs save_pass] != "" } {
	return [uplevel 2 $real_name]
    }

    rename pass save_pass
    rename ignore_pass pass

    set code [catch {uplevel 2 $real_name} result]

    rename pass ignore_pass
    rename save_pass pass

    if {$code == 1} {
	global errorInfo errorCode
	return -code error -errorinfo $errorInfo -errorcode $errorCode $result
    } elseif {$code > 1} {
	return -code $code $result
    }

    return $result
}

# A helper for gdb_caching_proc that handles the caching.

proc gdb_do_cache {name} {
    global gdb_data_cache objdir
    global GDB_PARALLEL

    # Normally, if we have a cached value, we skip computation and return
    # the cached value.  If set to 1, instead don't skip computation and
    # verify against the cached value.
    set cache_verify 0

    # Alternatively, set this to do cache_verify only for one proc.
    set cache_verify_proc ""
    if { $name == $cache_verify_proc } {
	set cache_verify 1
    }

    # See if some other process wrote the cache file.  Cache value per
    # "board" to handle runs with multiple options
    # (e.g. unix/{-m32,-64}) correctly.  We use "file join" here
    # because we later use this in a real filename.
    set cache_name [file join [target_info name] $name]

    set is_cached 0
    if {[info exists gdb_data_cache($cache_name)]} {
	set cached $gdb_data_cache($cache_name)
	verbose "$name: returning '$cached' from cache" 2
	if { $cache_verify == 0 } {
	    return $cached
	}
	set is_cached 1
    }

    if { $is_cached == 0 && [info exists GDB_PARALLEL] } {
	set cache_filename [make_gdb_parallel_path cache $cache_name]
	if {[file exists $cache_filename]} {
	    set fd [open $cache_filename]
	    set gdb_data_cache($cache_name) [read -nonewline $fd]
	    close $fd
	    set cached $gdb_data_cache($cache_name)
	    verbose "$name: returning '$cached' from file cache" 2
	    if { $cache_verify == 0 } {
		return $cached
	    }
	    set is_cached 1
	}
    }

    set real_name gdb_real__$name
    set gdb_data_cache($cache_name) [gdb_do_cache_wrap $real_name]
    if { $cache_verify == 1 && $is_cached == 1 } {
	set computed $gdb_data_cache($cache_name)
	if { $cached != $computed } {
	    error [join [list "Inconsistent results for $cache_name:"
			 "cached: $cached vs. computed: $computed"]]
	}
    }

    if {[info exists GDB_PARALLEL]} {
	verbose "$name: returning '$gdb_data_cache($cache_name)' and writing file" 2
	file mkdir [file dirname $cache_filename]
	# Make sure to write the results file atomically.
	set fd [open $cache_filename.[pid] w]
	puts $fd $gdb_data_cache($cache_name)
	close $fd
	file rename -force -- $cache_filename.[pid] $cache_filename
    }
    return $gdb_data_cache($cache_name)
}

# Define a new proc named NAME that takes no arguments.  BODY is the
# body of the proc.  The proc will evaluate BODY and cache the
# results, both in memory and, if GDB_PARALLEL is defined, in the
# filesystem for use across invocations of dejagnu.

proc gdb_caching_proc {name body} {
    # Define the underlying proc that we'll call.
    set real_name gdb_real__$name
    proc $real_name {} $body

    # Define the advertised proc.
    proc $name {} [list gdb_do_cache $name]
}