/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/lib/Support/GraphWriter.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- GraphWriter.cpp - Implements GraphWriter support routines ----------===// |
2 | | // |
3 | | // The LLVM Compiler Infrastructure |
4 | | // |
5 | | // This file is distributed under the University of Illinois Open Source |
6 | | // License. See LICENSE.TXT for details. |
7 | | // |
8 | | //===----------------------------------------------------------------------===// |
9 | | // |
10 | | // This file implements misc. GraphWriter support routines. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | #include "llvm/Support/GraphWriter.h" |
15 | | #include "llvm/ADT/SmallString.h" |
16 | | #include "llvm/ADT/SmallVector.h" |
17 | | #include "llvm/ADT/StringRef.h" |
18 | | #include "llvm/Config/config.h" |
19 | | #include "llvm/Support/CommandLine.h" |
20 | | #include "llvm/Support/Compiler.h" |
21 | | #include "llvm/Support/ErrorHandling.h" |
22 | | #include "llvm/Support/ErrorOr.h" |
23 | | #include "llvm/Support/FileSystem.h" |
24 | | #include "llvm/Support/Program.h" |
25 | | #include "llvm/Support/raw_ostream.h" |
26 | | #include <cassert> |
27 | | #include <system_error> |
28 | | #include <string> |
29 | | #include <vector> |
30 | | |
31 | | using namespace llvm; |
32 | | |
33 | | static cl::opt<bool> ViewBackground("view-background", cl::Hidden, |
34 | | cl::desc("Execute graph viewer in the background. Creates tmp file litter.")); |
35 | | |
36 | 19 | std::string llvm::DOT::EscapeString(const std::string &Label) { |
37 | 19 | std::string Str(Label); |
38 | 1.50k | for (unsigned i = 0; i != Str.length()1.50k ; ++i1.48k ) |
39 | 1.48k | switch (Str[i]) { |
40 | 0 | case '\n': |
41 | 0 | Str.insert(Str.begin()+i, '\\'); // Escape character... |
42 | 0 | ++i; |
43 | 0 | Str[i] = 'n'; |
44 | 0 | break; |
45 | 0 | case '\t': |
46 | 0 | Str.insert(Str.begin()+i, ' '); // Convert to two spaces |
47 | 0 | ++i; |
48 | 0 | Str[i] = ' '; |
49 | 0 | break; |
50 | 37 | case '\\': |
51 | 37 | if (i+1 != Str.length()) |
52 | 37 | switch (Str[i+1]) { |
53 | 37 | case 'l': continue; // don't disturb \l |
54 | 0 | case '|': 0 case '{': 0 case '}': |
55 | 0 | Str.erase(Str.begin()+i); continue; |
56 | 0 | default: break; |
57 | 0 | } |
58 | 0 | LLVM_FALLTHROUGH0 ; |
59 | 0 | case '{': 0 case '}': |
60 | 0 | case '<': 0 case '>': |
61 | 0 | case '|': 0 case '"': |
62 | 0 | Str.insert(Str.begin()+i, '\\'); // Escape character... |
63 | 0 | ++i; // don't infinite loop |
64 | 0 | break; |
65 | 1.48k | } |
66 | 19 | return Str; |
67 | 19 | } |
68 | | |
69 | | /// \brief Get a color string for this node number. Simply round-robin selects |
70 | | /// from a reasonable number of colors. |
71 | 0 | StringRef llvm::DOT::getColorString(unsigned ColorNumber) { |
72 | 0 | static const int NumColors = 20; |
73 | 0 | static const char* Colors[NumColors] = { |
74 | 0 | "aaaaaa", "aa0000", "00aa00", "aa5500", "0055ff", "aa00aa", "00aaaa", |
75 | 0 | "555555", "ff5555", "55ff55", "ffff55", "5555ff", "ff55ff", "55ffff", |
76 | 0 | "ffaaaa", "aaffaa", "ffffaa", "aaaaff", "ffaaff", "aaffff"}; |
77 | 0 | return Colors[ColorNumber % NumColors]; |
78 | 0 | } |
79 | | |
80 | 0 | std::string llvm::createGraphFilename(const Twine &Name, int &FD) { |
81 | 0 | FD = -1; |
82 | 0 | SmallString<128> Filename; |
83 | 0 | std::error_code EC = sys::fs::createTemporaryFile(Name, "dot", FD, Filename); |
84 | 0 | if (EC0 ) { |
85 | 0 | errs() << "Error: " << EC.message() << "\n"; |
86 | 0 | return ""; |
87 | 0 | } |
88 | 0 |
|
89 | 0 | errs() << "Writing '" << Filename << "'... "; |
90 | 0 | return Filename.str(); |
91 | 0 | } |
92 | | |
93 | | // Execute the graph viewer. Return true if there were errors. |
94 | | static bool ExecGraphViewer(StringRef ExecPath, std::vector<const char *> &args, |
95 | | StringRef Filename, bool wait, |
96 | 0 | std::string &ErrMsg) { |
97 | 0 | assert(args.back() == nullptr); |
98 | 0 | if (wait0 ) { |
99 | 0 | if (sys::ExecuteAndWait(ExecPath, args.data(), nullptr, {}, 0, 0, |
100 | 0 | &ErrMsg)) { |
101 | 0 | errs() << "Error: " << ErrMsg << "\n"; |
102 | 0 | return true; |
103 | 0 | } |
104 | 0 | sys::fs::remove(Filename); |
105 | 0 | errs() << " done. \n"; |
106 | 0 | } else { |
107 | 0 | sys::ExecuteNoWait(ExecPath, args.data(), nullptr, {}, 0, &ErrMsg); |
108 | 0 | errs() << "Remember to erase graph file: " << Filename << "\n"; |
109 | 0 | } |
110 | 0 | return false; |
111 | 0 | } |
112 | | |
113 | | namespace { |
114 | | |
115 | | struct GraphSession { |
116 | | std::string LogBuffer; |
117 | | |
118 | 0 | bool TryFindProgram(StringRef Names, std::string &ProgramPath) { |
119 | 0 | raw_string_ostream Log(LogBuffer); |
120 | 0 | SmallVector<StringRef, 8> parts; |
121 | 0 | Names.split(parts, '|'); |
122 | 0 | for (auto Name : parts) { |
123 | 0 | if (ErrorOr<std::string> P0 = sys::findProgramByName(Name)) { |
124 | 0 | ProgramPath = *P; |
125 | 0 | return true; |
126 | 0 | } |
127 | 0 | Log << " Tried '" << Name << "'\n"; |
128 | 0 | } |
129 | 0 | return false; |
130 | 0 | } |
131 | | }; |
132 | | |
133 | | } // end anonymous namespace |
134 | | |
135 | 0 | static const char *getProgramName(GraphProgram::Name program) { |
136 | 0 | switch (program) { |
137 | 0 | case GraphProgram::DOT: |
138 | 0 | return "dot"; |
139 | 0 | case GraphProgram::FDP: |
140 | 0 | return "fdp"; |
141 | 0 | case GraphProgram::NEATO: |
142 | 0 | return "neato"; |
143 | 0 | case GraphProgram::TWOPI: |
144 | 0 | return "twopi"; |
145 | 0 | case GraphProgram::CIRCO: |
146 | 0 | return "circo"; |
147 | 0 | } |
148 | 0 | llvm_unreachable0 ("bad kind"); |
149 | 0 | } |
150 | | |
151 | | bool llvm::DisplayGraph(StringRef FilenameRef, bool wait, |
152 | 0 | GraphProgram::Name program) { |
153 | 0 | std::string Filename = FilenameRef; |
154 | 0 | std::string ErrMsg; |
155 | 0 | std::string ViewerPath; |
156 | 0 | GraphSession S; |
157 | 0 |
|
158 | 0 | #ifdef __APPLE__ |
159 | 0 | wait &= !ViewBackground; |
160 | 0 | if (S.TryFindProgram("open", ViewerPath)0 ) { |
161 | 0 | std::vector<const char *> args; |
162 | 0 | args.push_back(ViewerPath.c_str()); |
163 | 0 | if (wait) |
164 | 0 | args.push_back("-W"); |
165 | 0 | args.push_back(Filename.c_str()); |
166 | 0 | args.push_back(nullptr); |
167 | 0 | errs() << "Trying 'open' program... "; |
168 | 0 | if (!ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg)) |
169 | 0 | return false; |
170 | 0 | } |
171 | 0 | #endif |
172 | 0 | if (0 S.TryFindProgram("xdg-open", ViewerPath)0 ) { |
173 | 0 | std::vector<const char *> args; |
174 | 0 | args.push_back(ViewerPath.c_str()); |
175 | 0 | args.push_back(Filename.c_str()); |
176 | 0 | args.push_back(nullptr); |
177 | 0 | errs() << "Trying 'xdg-open' program... "; |
178 | 0 | if (!ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg)) |
179 | 0 | return false; |
180 | 0 | } |
181 | 0 |
|
182 | 0 | // Graphviz |
183 | 0 | if (0 S.TryFindProgram("Graphviz", ViewerPath)0 ) { |
184 | 0 | std::vector<const char *> args; |
185 | 0 | args.push_back(ViewerPath.c_str()); |
186 | 0 | args.push_back(Filename.c_str()); |
187 | 0 | args.push_back(nullptr); |
188 | 0 |
|
189 | 0 | errs() << "Running 'Graphviz' program... "; |
190 | 0 | return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg); |
191 | 0 | } |
192 | 0 |
|
193 | 0 | // xdot |
194 | 0 | if (0 S.TryFindProgram("xdot|xdot.py", ViewerPath)0 ) { |
195 | 0 | std::vector<const char *> args; |
196 | 0 | args.push_back(ViewerPath.c_str()); |
197 | 0 | args.push_back(Filename.c_str()); |
198 | 0 |
|
199 | 0 | args.push_back("-f"); |
200 | 0 | args.push_back(getProgramName(program)); |
201 | 0 |
|
202 | 0 | args.push_back(nullptr); |
203 | 0 |
|
204 | 0 | errs() << "Running 'xdot.py' program... "; |
205 | 0 | return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg); |
206 | 0 | } |
207 | 0 |
|
208 | 0 | enum ViewerKind { |
209 | 0 | VK_None, |
210 | 0 | VK_OSXOpen, |
211 | 0 | VK_XDGOpen, |
212 | 0 | VK_Ghostview, |
213 | 0 | VK_CmdStart |
214 | 0 | }; |
215 | 0 | ViewerKind Viewer = VK_None; |
216 | 0 | #ifdef __APPLE__ |
217 | 0 | if (!Viewer && 0 S.TryFindProgram("open", ViewerPath)0 ) |
218 | 0 | Viewer = VK_OSXOpen; |
219 | 0 | #endif |
220 | 0 | if (!Viewer && 0 S.TryFindProgram("gv", ViewerPath)0 ) |
221 | 0 | Viewer = VK_Ghostview; |
222 | 0 | if (!Viewer && 0 S.TryFindProgram("xdg-open", ViewerPath)0 ) |
223 | 0 | Viewer = VK_XDGOpen; |
224 | | #ifdef LLVM_ON_WIN32 |
225 | | if (!Viewer && S.TryFindProgram("cmd", ViewerPath)) { |
226 | | Viewer = VK_CmdStart; |
227 | | } |
228 | | #endif |
229 | |
|
230 | 0 | // PostScript or PDF graph generator + PostScript/PDF viewer |
231 | 0 | std::string GeneratorPath; |
232 | 0 | if (Viewer && |
233 | 0 | (S.TryFindProgram(getProgramName(program), GeneratorPath) || |
234 | 0 | S.TryFindProgram("dot|fdp|neato|twopi|circo", GeneratorPath)0 )) { |
235 | 0 | std::string OutputFilename = |
236 | 0 | Filename + (Viewer == VK_CmdStart ? ".pdf"0 : ".ps"0 ); |
237 | 0 |
|
238 | 0 | std::vector<const char *> args; |
239 | 0 | args.push_back(GeneratorPath.c_str()); |
240 | 0 | if (Viewer == VK_CmdStart) |
241 | 0 | args.push_back("-Tpdf"); |
242 | 0 | else |
243 | 0 | args.push_back("-Tps"); |
244 | 0 | args.push_back("-Nfontname=Courier"); |
245 | 0 | args.push_back("-Gsize=7.5,10"); |
246 | 0 | args.push_back(Filename.c_str()); |
247 | 0 | args.push_back("-o"); |
248 | 0 | args.push_back(OutputFilename.c_str()); |
249 | 0 | args.push_back(nullptr); |
250 | 0 |
|
251 | 0 | errs() << "Running '" << GeneratorPath << "' program... "; |
252 | 0 |
|
253 | 0 | if (ExecGraphViewer(GeneratorPath, args, Filename, true, ErrMsg)) |
254 | 0 | return true; |
255 | 0 |
|
256 | 0 | // The lifetime of StartArg must include the call of ExecGraphViewer |
257 | 0 | // because the args are passed as vector of char*. |
258 | 0 | std::string StartArg; |
259 | 0 |
|
260 | 0 | args.clear(); |
261 | 0 | args.push_back(ViewerPath.c_str()); |
262 | 0 | switch (Viewer) { |
263 | 0 | case VK_OSXOpen: |
264 | 0 | args.push_back("-W"); |
265 | 0 | args.push_back(OutputFilename.c_str()); |
266 | 0 | break; |
267 | 0 | case VK_XDGOpen: |
268 | 0 | wait = false; |
269 | 0 | args.push_back(OutputFilename.c_str()); |
270 | 0 | break; |
271 | 0 | case VK_Ghostview: |
272 | 0 | args.push_back("--spartan"); |
273 | 0 | args.push_back(OutputFilename.c_str()); |
274 | 0 | break; |
275 | 0 | case VK_CmdStart: |
276 | 0 | args.push_back("/S"); |
277 | 0 | args.push_back("/C"); |
278 | 0 | StartArg = |
279 | 0 | (StringRef("start ") + (wait ? "/WAIT "0 : ""0 ) + OutputFilename).str(); |
280 | 0 | args.push_back(StartArg.c_str()); |
281 | 0 | break; |
282 | 0 | case VK_None: |
283 | 0 | llvm_unreachable("Invalid viewer"); |
284 | 0 | } |
285 | 0 | args.push_back(nullptr); |
286 | 0 |
|
287 | 0 | ErrMsg.clear(); |
288 | 0 | return ExecGraphViewer(ViewerPath, args, OutputFilename, wait, ErrMsg); |
289 | 0 | } |
290 | 0 |
|
291 | 0 | // dotty |
292 | 0 | if (0 S.TryFindProgram("dotty", ViewerPath)0 ) { |
293 | 0 | std::vector<const char *> args; |
294 | 0 | args.push_back(ViewerPath.c_str()); |
295 | 0 | args.push_back(Filename.c_str()); |
296 | 0 | args.push_back(nullptr); |
297 | 0 |
|
298 | 0 | // Dotty spawns another app and doesn't wait until it returns |
299 | | #ifdef LLVM_ON_WIN32 |
300 | | wait = false; |
301 | | #endif |
302 | | errs() << "Running 'dotty' program... "; |
303 | 0 | return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg); |
304 | 0 | } |
305 | 0 |
|
306 | 0 | errs() << "Error: Couldn't find a usable graph viewer program:\n"; |
307 | 0 | errs() << S.LogBuffer << "\n"; |
308 | 0 | return true; |
309 | 0 | } |