LLVM 24.0.0git
GraphWriter.cpp
Go to the documentation of this file.
1//===- GraphWriter.cpp - Implements GraphWriter support routines ----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements misc. GraphWriter support routines.
10//
11//===----------------------------------------------------------------------===//
12
14
15#include "DebugOptions.h"
16
19#include "llvm/ADT/StringRef.h"
20#include "llvm/Config/config.h"
27#include "llvm/Support/Path.h"
30
31#include <string>
32#include <system_error>
33#include <vector>
34
35using namespace llvm;
36
37#ifdef __APPLE__
38namespace {
39struct CreateViewBackground {
40 static void *call() {
41 return new cl::opt<bool>("view-background", cl::Hidden,
42 cl::desc("Execute graph viewer in the background. "
43 "Creates tmp file litter."));
44 }
45};
46} // namespace
47static ManagedStatic<cl::opt<bool>, CreateViewBackground> ViewBackground;
48#endif
49
50namespace {
51struct CreateDAGGraphWriteLocation {
52 static void *call() {
53 return new cl::opt<std::string>(
54 "dag-file-location", cl::Hidden,
55 cl::desc("Location to place the DAG graphs selected to be viewed"));
56 }
57};
58
59struct CreateNoOpenDAGViewer {
60 static void *call() {
61 return new cl::opt<bool>(
62 "no-open-dag-viewer", cl::Hidden,
63 cl::desc("Don't open the DAG viewer program, just write the file"),
64 cl::init(false));
65 }
66};
67} // namespace
68static ManagedStatic<cl::opt<std::string>, CreateDAGGraphWriteLocation>
70static ManagedStatic<cl::opt<bool>, CreateNoOpenDAGViewer> NoOpenDAGViewer;
71
73#ifdef __APPLE__
74 *ViewBackground;
75#endif
76
79}
80
81std::string llvm::DOT::EscapeString(const std::string &Label) {
82 std::string Str(Label);
83 for (unsigned i = 0; i != Str.length(); ++i)
84 switch (Str[i]) {
85 case '\n':
86 Str.insert(Str.begin()+i, '\\'); // Escape character...
87 ++i;
88 Str[i] = 'n';
89 break;
90 case '\t':
91 Str.insert(Str.begin()+i, ' '); // Convert to two spaces
92 ++i;
93 Str[i] = ' ';
94 break;
95 case '\\':
96 if (i+1 != Str.length())
97 switch (Str[i+1]) {
98 case 'l': continue; // don't disturb \l
99 case '|': case '{': case '}':
100 Str.erase(Str.begin()+i); continue;
101 default: break;
102 }
103 [[fallthrough]];
104 case '{': case '}':
105 case '<': case '>':
106 case '|': case '"':
107 Str.insert(Str.begin()+i, '\\'); // Escape character...
108 ++i; // don't infinite loop
109 break;
110 }
111 return Str;
112}
113
114/// Get a color string for this node number. Simply round-robin selects
115/// from a reasonable number of colors.
117 static const int NumColors = 20;
118 static const char* Colors[NumColors] = {
119 "aaaaaa", "aa0000", "00aa00", "aa5500", "0055ff", "aa00aa", "00aaaa",
120 "555555", "ff5555", "55ff55", "ffff55", "5555ff", "ff55ff", "55ffff",
121 "ffaaaa", "aaffaa", "ffffaa", "aaaaff", "ffaaff", "aaffff"};
122 return Colors[ColorNumber % NumColors];
123}
124
125static std::string replaceIllegalFilenameChars(std::string Filename,
126 const char ReplacementChar) {
127 std::string IllegalChars =
128 is_style_windows(sys::path::Style::native) ? "\\/:?\"<>|" : "/";
129
130 for (char IllegalChar : IllegalChars)
131 llvm::replace(Filename, IllegalChar, ReplacementChar);
132
133 return Filename;
134}
135
136std::string llvm::createGraphFilename(const Twine &Name, int &FD) {
137 FD = -1;
139
140 // Windows can't always handle long paths, so limit the length of the name.
141 std::string N = Name.str();
142 if (N.size() > 140)
143 N.resize(140);
144
145 // Replace illegal characters in graph Filename with '_' if needed
146 std::string CleansedName = replaceIllegalFilenameChars(N, '_');
147
148 // If no directory is specified, use the default tmp directory
149 // If a directory is specified, use that
150 std::error_code EC;
151 if (DAGGraphWriteLocation->empty()) {
152 EC = sys::fs::createTemporaryFile(CleansedName, "dot", FD, Filename);
153 } else {
154 llvm::SmallString<128> realpath; // Expand and correct given path
155 auto path_EC = sys::fs::real_path(*DAGGraphWriteLocation, realpath, true);
156 if (path_EC) {
157 errs() << "Error resolving path: " << path_EC.message() << "\n";
158 return "";
159 }
160
162 realpath + "/" + CleansedName + "-%%%%%%.dot", FD, Filename);
163 }
164
165 if (EC) {
166 errs() << "Error: " << EC.message() << "\n";
167 return "";
168 }
169
170 errs() << "Writing '" << Filename << "'... ";
171 return std::string(Filename);
172}
173
174// Execute the graph viewer. Return true if there were errors.
175static bool ExecGraphViewer(StringRef ExecPath, std::vector<StringRef> &args,
176 StringRef Filename, bool wait,
177 std::string &ErrMsg) {
178 if (wait) {
179 if (sys::ExecuteAndWait(ExecPath, args, std::nullopt, {}, 0, 0, &ErrMsg)) {
180 errs() << "Error: " << ErrMsg << "\n";
181 return true;
182 }
184 errs() << " done. \n";
185 } else {
186 sys::ExecuteNoWait(ExecPath, args, std::nullopt, {}, 0, &ErrMsg);
187 errs() << "Remember to erase graph file: " << Filename << "\n";
188 }
189 return false;
190}
191
192namespace {
193
194struct GraphSession {
195 std::string LogBuffer;
196
197 bool TryFindProgram(StringRef Names, std::string &ProgramPath) {
198 raw_string_ostream Log(LogBuffer);
200 Names.split(parts, '|');
201 for (auto Name : parts) {
202 if (ErrorOr<std::string> P = sys::findProgramByName(Name)) {
203 ProgramPath = *P;
204 return true;
205 }
206 Log << " Tried '" << Name << "'\n";
207 }
208 return false;
209 }
210};
211
212} // end anonymous namespace
213
214static const char *getProgramName(GraphProgram::Name program) {
215 switch (program) {
217 return "dot";
219 return "fdp";
221 return "neato";
223 return "twopi";
225 return "circo";
226 }
227 llvm_unreachable("bad kind");
228}
229
230bool llvm::DisplayGraph(StringRef FilenameRef, bool wait,
231 GraphProgram::Name program) {
232 std::string Filename = std::string(FilenameRef);
233 std::string ErrMsg;
234 std::string ViewerPath;
235 GraphSession S;
236
237 if (*NoOpenDAGViewer) {
238 errs() << "Not opening graph viewer program as per options.\n";
239 return true;
240 }
241
242#ifdef __APPLE__
243 wait &= !*ViewBackground;
244 if (S.TryFindProgram("open", ViewerPath)) {
245 std::vector<StringRef> args;
246 args.push_back(ViewerPath);
247 if (wait)
248 args.push_back("-W");
249 args.push_back(Filename);
250 errs() << "Trying 'open' program... ";
251 if (!ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg))
252 return false;
253 }
254#endif
255 if (S.TryFindProgram("xdg-open", ViewerPath)) {
256 std::vector<StringRef> args;
257 args.push_back(ViewerPath);
258 args.push_back(Filename);
259 errs() << "Trying 'xdg-open' program... ";
260 if (!ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg))
261 return false;
262 }
263
264 // Graphviz
265 if (S.TryFindProgram("Graphviz", ViewerPath)) {
266 std::vector<StringRef> args;
267 args.push_back(ViewerPath);
268 args.push_back(Filename);
269
270 errs() << "Running 'Graphviz' program... ";
271 return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg);
272 }
273
274 // xdot
275 if (S.TryFindProgram("xdot|xdot.py", ViewerPath)) {
276 std::vector<StringRef> args;
277 args.push_back(ViewerPath);
278 args.push_back(Filename);
279
280 args.push_back("-f");
281 args.push_back(getProgramName(program));
282
283 errs() << "Running 'xdot.py' program... ";
284 return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg);
285 }
286
287 enum ViewerKind {
288 VK_None,
289 VK_OSXOpen,
290 VK_XDGOpen,
291 VK_Ghostview,
292 VK_CmdStart
293 };
294 ViewerKind Viewer = VK_None;
295#ifdef __APPLE__
296 if (!Viewer && S.TryFindProgram("open", ViewerPath))
297 Viewer = VK_OSXOpen;
298#endif
299 if (!Viewer && S.TryFindProgram("gv", ViewerPath))
300 Viewer = VK_Ghostview;
301 if (!Viewer && S.TryFindProgram("xdg-open", ViewerPath))
302 Viewer = VK_XDGOpen;
303#ifdef _WIN32
304 if (!Viewer && S.TryFindProgram("cmd", ViewerPath)) {
305 Viewer = VK_CmdStart;
306 }
307#endif
308
309 // PostScript or PDF graph generator + PostScript/PDF viewer
310 std::string GeneratorPath;
311 if (Viewer &&
312 (S.TryFindProgram(getProgramName(program), GeneratorPath) ||
313 S.TryFindProgram("dot|fdp|neato|twopi|circo", GeneratorPath))) {
314 std::string OutputFilename =
315 Filename + (Viewer == VK_CmdStart ? ".pdf" : ".ps");
316
317 std::vector<StringRef> args;
318 args.push_back(GeneratorPath);
319 if (Viewer == VK_CmdStart)
320 args.push_back("-Tpdf");
321 else
322 args.push_back("-Tps");
323 args.push_back("-Nfontname=Courier");
324 args.push_back("-Gsize=7.5,10");
325 args.push_back(Filename);
326 args.push_back("-o");
327 args.push_back(OutputFilename);
328
329 errs() << "Running '" << GeneratorPath << "' program... ";
330
331 if (ExecGraphViewer(GeneratorPath, args, Filename, true, ErrMsg))
332 return true;
333
334 // The lifetime of StartArg must include the call of ExecGraphViewer
335 // because the args are passed as vector of char*.
336 std::string StartArg;
337
338 args.clear();
339 args.push_back(ViewerPath);
340 switch (Viewer) {
341 case VK_OSXOpen:
342 args.push_back("-W");
343 args.push_back(OutputFilename);
344 break;
345 case VK_XDGOpen:
346 wait = false;
347 args.push_back(OutputFilename);
348 break;
349 case VK_Ghostview:
350 args.push_back("--spartan");
351 args.push_back(OutputFilename);
352 break;
353 case VK_CmdStart:
354 args.push_back("/S");
355 args.push_back("/C");
356 StartArg =
357 (StringRef("start ") + (wait ? "/WAIT " : "") + OutputFilename).str();
358 args.push_back(StartArg);
359 break;
360 case VK_None:
361 llvm_unreachable("Invalid viewer");
362 }
363
364 ErrMsg.clear();
365 return ExecGraphViewer(ViewerPath, args, OutputFilename, wait, ErrMsg);
366 }
367
368 // dotty
369 if (S.TryFindProgram("dotty", ViewerPath)) {
370 std::vector<StringRef> args;
371 args.push_back(ViewerPath);
372 args.push_back(Filename);
373
374// Dotty spawns another app and doesn't wait until it returns
375#ifdef _WIN32
376 wait = false;
377#endif
378 errs() << "Running 'dotty' program... ";
379 return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg);
380 }
381
382 errs() << "Error: Couldn't find a usable graph viewer program:\n";
383 errs() << S.LogBuffer << "\n";
384 return true;
385}
Provides ErrorOr<T> smart pointer.
static std::string replaceIllegalFilenameChars(std::string Filename, const char ReplacementChar)
static bool ExecGraphViewer(StringRef ExecPath, std::vector< StringRef > &args, StringRef Filename, bool wait, std::string &ErrMsg)
static ManagedStatic< cl::opt< std::string >, CreateDAGGraphWriteLocation > DAGGraphWriteLocation
static ManagedStatic< cl::opt< bool >, CreateNoOpenDAGViewer > NoOpenDAGViewer
static const char * getProgramName(GraphProgram::Name program)
static cl::opt< std::string > OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), cl::init("-"))
nvptx lower args
static constexpr StringLiteral Filename
#define P(N)
This file defines the SmallString class.
This file defines the SmallVector class.
ManagedStatic - This transparently changes the behavior of global statics to be lazily constructed on...
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
Definition StringRef.h:736
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
LLVM_ABI std::string EscapeString(const std::string &Label)
LLVM_ABI StringRef getColorString(unsigned NodeNumber)
Get a color string for this node number.
template class LLVM_TEMPLATE_ABI opt< bool >
initializer< Ty > init(const Ty &Val)
template class LLVM_TEMPLATE_ABI opt< std::string >
LLVM_ABI std::error_code createUniqueFile(const Twine &Model, int &ResultFD, SmallVectorImpl< char > &ResultPath, OpenFlags Flags=OF_None, unsigned Mode=all_read|all_write)
Create a uniquely named file.
Definition Path.cpp:891
LLVM_ABI std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
LLVM_ABI std::error_code real_path(const Twine &path, SmallVectorImpl< char > &output, bool expand_tilde=false)
Collapse all .
LLVM_ABI std::error_code createTemporaryFile(const Twine &Prefix, StringRef Suffix, int &ResultFD, SmallVectorImpl< char > &ResultPath, OpenFlags Flags=OF_None)
Create a file in the system temporary directory.
Definition Path.cpp:936
LLVM_ABI ProcessInfo ExecuteNoWait(StringRef Program, ArrayRef< StringRef > Args, std::optional< ArrayRef< StringRef > > Env, ArrayRef< std::optional< StringRef > > Redirects={}, unsigned MemoryLimit=0, std::string *ErrMsg=nullptr, bool *ExecutionFailed=nullptr, BitVector *AffinityMask=nullptr, bool DetachProcess=false)
Similar to ExecuteAndWait, but returns immediately.
Definition Program.cpp:57
LLVM_ABI ErrorOr< std::string > findProgramByName(StringRef Name, ArrayRef< StringRef > Paths={})
Find the first executable file Name in Paths.
LLVM_ABI int ExecuteAndWait(StringRef Program, ArrayRef< StringRef > Args, std::optional< ArrayRef< StringRef > > Env=std::nullopt, ArrayRef< std::optional< StringRef > > Redirects={}, unsigned SecondsToWait=0, unsigned MemoryLimit=0, std::string *ErrMsg=nullptr, bool *ExecutionFailed=nullptr, std::optional< ProcessStatistics > *ProcStat=nullptr, BitVector *AffinityMask=nullptr)
This function executes the program using the arguments provided.
Definition Program.cpp:32
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI bool DisplayGraph(StringRef Filename, bool wait=true, GraphProgram::Name program=GraphProgram::DOT)
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
LLVM_ABI std::string createGraphFilename(const Twine &Name, int &FD)
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
void replace(R &&Range, const T &OldValue, const T &NewValue)
Provide wrappers to std::replace which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1910
void initGraphWriterOptions()
#define N