LLVM  10.0.0svn
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 #include "llvm/ADT/SmallString.h"
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/Config/config.h"
19 #include "llvm/Support/Compiler.h"
21 #include "llvm/Support/ErrorOr.h"
23 #include "llvm/Support/Program.h"
25 #include <cassert>
26 #include <system_error>
27 #include <string>
28 #include <vector>
29 
30 using namespace llvm;
31 
32 static cl::opt<bool> ViewBackground("view-background", cl::Hidden,
33  cl::desc("Execute graph viewer in the background. Creates tmp file litter."));
34 
35 std::string llvm::DOT::EscapeString(const std::string &Label) {
36  std::string Str(Label);
37  for (unsigned i = 0; i != Str.length(); ++i)
38  switch (Str[i]) {
39  case '\n':
40  Str.insert(Str.begin()+i, '\\'); // Escape character...
41  ++i;
42  Str[i] = 'n';
43  break;
44  case '\t':
45  Str.insert(Str.begin()+i, ' '); // Convert to two spaces
46  ++i;
47  Str[i] = ' ';
48  break;
49  case '\\':
50  if (i+1 != Str.length())
51  switch (Str[i+1]) {
52  case 'l': continue; // don't disturb \l
53  case '|': case '{': case '}':
54  Str.erase(Str.begin()+i); continue;
55  default: break;
56  }
58  case '{': case '}':
59  case '<': case '>':
60  case '|': case '"':
61  Str.insert(Str.begin()+i, '\\'); // Escape character...
62  ++i; // don't infinite loop
63  break;
64  }
65  return Str;
66 }
67 
68 /// Get a color string for this node number. Simply round-robin selects
69 /// from a reasonable number of colors.
70 StringRef llvm::DOT::getColorString(unsigned ColorNumber) {
71  static const int NumColors = 20;
72  static const char* Colors[NumColors] = {
73  "aaaaaa", "aa0000", "00aa00", "aa5500", "0055ff", "aa00aa", "00aaaa",
74  "555555", "ff5555", "55ff55", "ffff55", "5555ff", "ff55ff", "55ffff",
75  "ffaaaa", "aaffaa", "ffffaa", "aaaaff", "ffaaff", "aaffff"};
76  return Colors[ColorNumber % NumColors];
77 }
78 
79 std::string llvm::createGraphFilename(const Twine &Name, int &FD) {
80  FD = -1;
81  SmallString<128> Filename;
82  std::error_code EC = sys::fs::createTemporaryFile(Name, "dot", FD, Filename);
83  if (EC) {
84  errs() << "Error: " << EC.message() << "\n";
85  return "";
86  }
87 
88  errs() << "Writing '" << Filename << "'... ";
89  return Filename.str();
90 }
91 
92 // Execute the graph viewer. Return true if there were errors.
93 static bool ExecGraphViewer(StringRef ExecPath, std::vector<StringRef> &args,
94  StringRef Filename, bool wait,
95  std::string &ErrMsg) {
96  if (wait) {
97  if (sys::ExecuteAndWait(ExecPath, args, None, {}, 0, 0, &ErrMsg)) {
98  errs() << "Error: " << ErrMsg << "\n";
99  return true;
100  }
101  sys::fs::remove(Filename);
102  errs() << " done. \n";
103  } else {
104  sys::ExecuteNoWait(ExecPath, args, None, {}, 0, &ErrMsg);
105  errs() << "Remember to erase graph file: " << Filename << "\n";
106  }
107  return false;
108 }
109 
110 namespace {
111 
112 struct GraphSession {
113  std::string LogBuffer;
114 
115  bool TryFindProgram(StringRef Names, std::string &ProgramPath) {
116  raw_string_ostream Log(LogBuffer);
118  Names.split(parts, '|');
119  for (auto Name : parts) {
121  ProgramPath = *P;
122  return true;
123  }
124  Log << " Tried '" << Name << "'\n";
125  }
126  return false;
127  }
128 };
129 
130 } // end anonymous namespace
131 
132 static const char *getProgramName(GraphProgram::Name program) {
133  switch (program) {
134  case GraphProgram::DOT:
135  return "dot";
136  case GraphProgram::FDP:
137  return "fdp";
138  case GraphProgram::NEATO:
139  return "neato";
140  case GraphProgram::TWOPI:
141  return "twopi";
142  case GraphProgram::CIRCO:
143  return "circo";
144  }
145  llvm_unreachable("bad kind");
146 }
147 
148 bool llvm::DisplayGraph(StringRef FilenameRef, bool wait,
149  GraphProgram::Name program) {
150  std::string Filename = FilenameRef;
151  std::string ErrMsg;
152  std::string ViewerPath;
153  GraphSession S;
154 
155 #ifdef __APPLE__
156  wait &= !ViewBackground;
157  if (S.TryFindProgram("open", ViewerPath)) {
158  std::vector<StringRef> args;
159  args.push_back(ViewerPath);
160  if (wait)
161  args.push_back("-W");
162  args.push_back(Filename);
163  errs() << "Trying 'open' program... ";
164  if (!ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg))
165  return false;
166  }
167 #endif
168  if (S.TryFindProgram("xdg-open", ViewerPath)) {
169  std::vector<StringRef> args;
170  args.push_back(ViewerPath);
171  args.push_back(Filename);
172  errs() << "Trying 'xdg-open' program... ";
173  if (!ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg))
174  return false;
175  }
176 
177  // Graphviz
178  if (S.TryFindProgram("Graphviz", ViewerPath)) {
179  std::vector<StringRef> args;
180  args.push_back(ViewerPath);
181  args.push_back(Filename);
182 
183  errs() << "Running 'Graphviz' program... ";
184  return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg);
185  }
186 
187  // xdot
188  if (S.TryFindProgram("xdot|xdot.py", ViewerPath)) {
189  std::vector<StringRef> args;
190  args.push_back(ViewerPath);
191  args.push_back(Filename);
192 
193  args.push_back("-f");
194  args.push_back(getProgramName(program));
195 
196  errs() << "Running 'xdot.py' program... ";
197  return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg);
198  }
199 
200  enum ViewerKind {
201  VK_None,
202  VK_OSXOpen,
203  VK_XDGOpen,
204  VK_Ghostview,
205  VK_CmdStart
206  };
207  ViewerKind Viewer = VK_None;
208 #ifdef __APPLE__
209  if (!Viewer && S.TryFindProgram("open", ViewerPath))
210  Viewer = VK_OSXOpen;
211 #endif
212  if (!Viewer && S.TryFindProgram("gv", ViewerPath))
213  Viewer = VK_Ghostview;
214  if (!Viewer && S.TryFindProgram("xdg-open", ViewerPath))
215  Viewer = VK_XDGOpen;
216 #ifdef _WIN32
217  if (!Viewer && S.TryFindProgram("cmd", ViewerPath)) {
218  Viewer = VK_CmdStart;
219  }
220 #endif
221 
222  // PostScript or PDF graph generator + PostScript/PDF viewer
223  std::string GeneratorPath;
224  if (Viewer &&
225  (S.TryFindProgram(getProgramName(program), GeneratorPath) ||
226  S.TryFindProgram("dot|fdp|neato|twopi|circo", GeneratorPath))) {
227  std::string OutputFilename =
228  Filename + (Viewer == VK_CmdStart ? ".pdf" : ".ps");
229 
230  std::vector<StringRef> args;
231  args.push_back(GeneratorPath);
232  if (Viewer == VK_CmdStart)
233  args.push_back("-Tpdf");
234  else
235  args.push_back("-Tps");
236  args.push_back("-Nfontname=Courier");
237  args.push_back("-Gsize=7.5,10");
238  args.push_back(Filename);
239  args.push_back("-o");
240  args.push_back(OutputFilename);
241 
242  errs() << "Running '" << GeneratorPath << "' program... ";
243 
244  if (ExecGraphViewer(GeneratorPath, args, Filename, true, ErrMsg))
245  return true;
246 
247  // The lifetime of StartArg must include the call of ExecGraphViewer
248  // because the args are passed as vector of char*.
249  std::string StartArg;
250 
251  args.clear();
252  args.push_back(ViewerPath);
253  switch (Viewer) {
254  case VK_OSXOpen:
255  args.push_back("-W");
256  args.push_back(OutputFilename);
257  break;
258  case VK_XDGOpen:
259  wait = false;
260  args.push_back(OutputFilename);
261  break;
262  case VK_Ghostview:
263  args.push_back("--spartan");
264  args.push_back(OutputFilename);
265  break;
266  case VK_CmdStart:
267  args.push_back("/S");
268  args.push_back("/C");
269  StartArg =
270  (StringRef("start ") + (wait ? "/WAIT " : "") + OutputFilename).str();
271  args.push_back(StartArg);
272  break;
273  case VK_None:
274  llvm_unreachable("Invalid viewer");
275  }
276 
277  ErrMsg.clear();
278  return ExecGraphViewer(ViewerPath, args, OutputFilename, wait, ErrMsg);
279  }
280 
281  // dotty
282  if (S.TryFindProgram("dotty", ViewerPath)) {
283  std::vector<StringRef> args;
284  args.push_back(ViewerPath);
285  args.push_back(Filename);
286 
287 // Dotty spawns another app and doesn't wait until it returns
288 #ifdef _WIN32
289  wait = false;
290 #endif
291  errs() << "Running 'dotty' program... ";
292  return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg);
293  }
294 
295  errs() << "Error: Couldn't find a usable graph viewer program:\n";
296  errs() << S.LogBuffer << "\n";
297  return true;
298 }
const NoneType None
Definition: None.h:23
Represents either an error or a value T.
Definition: ErrorOr.h:56
raw_ostream & errs()
This returns a reference to a raw_ostream for standard error.
static cl::opt< bool > ViewBackground("view-background", cl::Hidden, cl::desc("Execute graph viewer in the background. Creates tmp file litter."))
static const char * getProgramName(GraphProgram::Name program)
This class represents lattice values for constants.
Definition: AllocatorList.h:23
ProcessInfo ExecuteNoWait(StringRef Program, ArrayRef< StringRef > Args, Optional< ArrayRef< StringRef >> Env, ArrayRef< Optional< StringRef >> Redirects={}, unsigned MemoryLimit=0, std::string *ErrMsg=nullptr, bool *ExecutionFailed=nullptr)
Similar to ExecuteAndWait, but returns immediately.
Definition: Program.cpp:51
amdgpu Simplify well known AMD library false FunctionCallee Value const Twine & Name
std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
ErrorOr< std::string > findProgramByName(StringRef Name, ArrayRef< StringRef > Paths={})
Find the first executable file Name in Paths.
int ExecuteAndWait(StringRef Program, ArrayRef< StringRef > Args, Optional< ArrayRef< StringRef >> Env=None, ArrayRef< Optional< StringRef >> Redirects={}, unsigned SecondsToWait=0, unsigned MemoryLimit=0, std::string *ErrMsg=nullptr, bool *ExecutionFailed=nullptr)
This function executes the program using the arguments provided.
Definition: Program.cpp:30
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:80
static cl::opt< std::string > OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), cl::init("-"))
StringRef str() const
Explicit conversion to StringRef.
Definition: SmallString.h:266
#define P(N)
std::string EscapeString(const std::string &Label)
Definition: GraphWriter.cpp:35
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
This is a &#39;vector&#39; (really, a variable-sized array), optimized for the case when the array is small...
Definition: SmallVector.h:837
std::string createGraphFilename(const Twine &Name, int &FD)
Definition: GraphWriter.cpp:79
LLVM_NODISCARD std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
Definition: StringRef.h:696
StringRef getColorString(unsigned NodeNumber)
Get a color string for this node number.
Definition: GraphWriter.cpp:70
bool DisplayGraph(StringRef Filename, bool wait=true, GraphProgram::Name program=GraphProgram::DOT)
std::error_code createTemporaryFile(const Twine &Prefix, StringRef Suffix, int &ResultFD, SmallVectorImpl< char > &ResultPath)
Create a file in the system temporary directory.
Definition: Path.cpp:812
Provides ErrorOr<T> smart pointer.
static bool ExecGraphViewer(StringRef ExecPath, std::vector< StringRef > &args, StringRef Filename, bool wait, std::string &ErrMsg)
Definition: GraphWriter.cpp:93
A raw_ostream that writes to an std::string.
Definition: raw_ostream.h:482
#define LLVM_FALLTHROUGH
LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements.
Definition: Compiler.h:250
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:48