-
Notifications
You must be signed in to change notification settings - Fork 1
/
instrumenter.go
194 lines (168 loc) · 5.13 KB
/
instrumenter.go
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package instrumenter
/*
Copyright (c) 2023, Erik Kassubek
All rights reserved.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
*/
/*
Author: Erik Kassubek <erik-kassubek@t-online.de>
Package: dedego-instrumenter
Project: Dynamic Analysis to detect potential deadlocks in concurrent Go programs
*/
/*
instrumenter.go
instrument files to work with the
"github.com/ErikKassubek/deadlockDetectorGo/src/dedego" libraries
*/
import (
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"io/ioutil"
"os"
"strings"
"github.com/ErikKassubek/deadlockDetectorGo/src/gui"
"golang.org/x/tools/go/ast/astutil"
)
/*
Function to perform instrumentation of all list of files
@param file_paths []string: list of file names to instrument
@param gui *gui.GuiElements: gui elements to display output
@param status *gui.Status: status of the program
@return map[int]int: map of select id to size
@return error: error or nil
*/
func InstrumentFiles(elements *gui.GuiElements,
status *gui.Status) (map[int]int, error) {
file_paths, err := getAllFiles(status)
if err != nil {
elements.AddToOutput("Failed to get all files\n")
return nil, err
}
for i, file := range file_paths {
if status.Instrument {
elements.Output.SetText(elements.Output.Text() +
fmt.Sprintf("Instrumenting file %s.\n", file))
} else {
elements.Output.SetText(elements.Output.Text() +
fmt.Sprintf("Analysing file %s.\n", file))
}
elements.OutputScroll.ScrollToBottom()
// instrument the file
err := instrument_file(file, status)
if err != nil {
e := "Could not intrument file" + file + ".\n" + err.Error()
elements.AddToOutput(e)
return nil, err
}
prog := float64(i+1) / float64(len(file_paths))
elements.ProgressInst.SetValue(prog)
}
// create turn select_ops into map
select_map := make(map[int]int)
for _, op := range select_ops {
select_map[op.id] = op.size
}
return select_map, nil
}
/*
Function to instrument a given file.
@param file_path string: path to the file
@return error: error or nil
*/
func instrument_file(file_path string, status *gui.Status) error {
// create output file
output_file, err := os.Create(status.Output + get_relative_path(file_path, status))
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create output file %s.\n",
file_path)
return err
}
defer output_file.Close()
// copy mod and sum files
if file_path[len(file_path)-3:] != ".go" {
content, err := ioutil.ReadFile(file_path)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to read file %s.\n", file_path)
return err
}
_, err = output_file.Write(content)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to write to output file %s.\n",
file_path)
return err
}
return nil
}
// instrument go files
err = instrument_go_file(file_path, status)
if err != nil {
fmt.Fprintf(os.Stderr, "Could not instrument %s\n", file_path)
return err
}
return nil
}
/*
Function to instrument a go file.
@param file_path string: path to the file
@return error: error or nil
*/
func instrument_go_file(file_path string, status *gui.Status) error {
// get the ASP of the file
astSet := token.NewFileSet()
f, err := parser.ParseFile(astSet, file_path, nil, 0)
if err != nil {
return err
}
imports := get_imports(f)
instrument_chan(f, astSet, status.SettingMaxTime,
status.SettingMaxSelectTime, imports)
instrument_mutex(f, astSet, imports)
// print changed ast to output file
output_path := status.Output + get_relative_path(file_path, status)
output_file, err := os.OpenFile(output_path, os.O_WRONLY, os.ModePerm)
if err != nil {
fmt.Fprintf(os.Stderr, "Could not open output file %s\n", output_path)
return err
}
defer output_file.Close()
if err := printer.Fprint(output_file, astSet, f); err != nil {
return err
}
return nil
}
func get_imports(f *ast.File) []string {
res := []string{}
astutil.Apply(f, nil, func(c *astutil.Cursor) bool {
n := c.Node()
switch d := n.(type) {
case *ast.ImportSpec:
if d.Name != nil {
res = append(res, d.Name.Name)
} else {
path := strings.Split(d.Path.Value, "/")
name := strings.ReplaceAll(path[len(path)-1], "\"", "")
res = append(res, name)
}
}
return true
})
return res
}
func get_relative_path(file string, status *gui.Status) string {
totalPathLen := len(strings.Split(status.FolderPath, string(os.PathSeparator)))
pathSplit := strings.Split(file, string(os.PathSeparator))
return string(os.PathSeparator) + strings.Join(pathSplit[totalPathLen-1:],
string(os.PathSeparator))
}