LLVM  14.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 
17 #include "llvm/ADT/SmallString.h"
18 #include "llvm/ADT/SmallVector.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Config/config.h"
22 #include "llvm/Support/Compiler.h"
24 #include "llvm/Support/ErrorOr.h"
26 #include "llvm/Support/Program.h"
28 #include <cassert>
29 #include <system_error>
30 #include <string>
31 #include <vector>
32 
33 using namespace llvm;
34 
35 #ifdef __APPLE__
36 namespace {
37 struct CreateViewBackground {
38  static void *call() {
39  return new cl::opt<bool>("view-background", cl::Hidden,
40  cl::desc("Execute graph viewer in the background. "
41  "Creates tmp file litter."));
42  }
43 };
44 } // namespace
45 static ManagedStatic<cl::opt<bool>, CreateViewBackground> ViewBackground;
46 void llvm::initGraphWriterOptions() { *ViewBackground; }
47 #else
49 #endif
50 
51 std::string llvm::DOT::EscapeString(const std::string &Label) {
52  std::string Str(Label);
53  for (unsigned i = 0; i != Str.length(); ++i)
54  switch (Str[i]) {
55  case '\n':
56  Str.insert(Str.begin()+i, '\\'); // Escape character...
57  ++i;
58  Str[i] = 'n';
59  break;
60  case '\t':
61  Str.insert(Str.begin()+i, ' '); // Convert to two spaces
62  ++i;
63  Str[i] = ' ';
64  break;
65  case '\\':
66  if (i+1 != Str.length())
67  switch (Str[i+1]) {
68  case 'l': continue; // don't disturb \l
69  case '|': case '{': case '}':
70  Str.erase(Str.begin()+i); continue;
71  default: break;
72  }
74  case '{': case '}':
75  case '<': case '>':
76  case '|': case '"':
77  Str.insert(Str.begin()+i, '\\'); // Escape character...
78  ++i; // don't infinite loop
79  break;
80  }
81  return Str;
82 }
83 
84 /// Get a color string for this node number. Simply round-robin selects
85 /// from a reasonable number of colors.
86 StringRef llvm::DOT::getColorString(unsigned ColorNumber) {
87  static const int NumColors = 20;
88  static const char* Colors[NumColors] = {
89  "aaaaaa", "aa0000", "00aa00", "aa5500", "0055ff", "aa00aa", "00aaaa",
90  "555555", "ff5555", "55ff55", "ffff55", "5555ff", "ff55ff", "55ffff",
91  "ffaaaa", "aaffaa", "ffffaa", "aaaaff", "ffaaff", "aaffff"};
92  return Colors[ColorNumber % NumColors];
93 }
94 
95 static std::string replaceIllegalFilenameChars(std::string Filename,
96  const char ReplacementChar) {
97 #ifdef _WIN32
98  std::string IllegalChars = "\\/:?\"<>|";
99 #else
100  std::string IllegalChars = "/";
101 #endif
102 
103  for (char IllegalChar : IllegalChars) {
104  std::replace(Filename.begin(), Filename.end(), IllegalChar,
105  ReplacementChar);
106  }
107 
108  return Filename;
109 }
110 
111 std::string llvm::createGraphFilename(const Twine &Name, int &FD) {
112  FD = -1;
113  SmallString<128> Filename;
114 
115  // Windows can't always handle long paths, so limit the length of the name.
116  std::string N = Name.str();
117  N = N.substr(0, std::min<std::size_t>(N.size(), 140));
118 
119  // Replace illegal characters in graph Filename with '_' if needed
120  std::string CleansedName = replaceIllegalFilenameChars(N, '_');
121 
122  std::error_code EC =
123  sys::fs::createTemporaryFile(CleansedName, "dot", FD, Filename);
124  if (EC) {
125  errs() << "Error: " << EC.message() << "\n";
126  return "";
127  }
128 
129  errs() << "Writing '" << Filename << "'... ";
130  return std::string(Filename.str());
131 }
132 
133 // Execute the graph viewer. Return true if there were errors.
134 static bool ExecGraphViewer(StringRef ExecPath, std::vector<StringRef> &args,
135  StringRef Filename, bool wait,
136  std::string &ErrMsg) {
137  if (wait) {
138  if (sys::ExecuteAndWait(ExecPath, args, None, {}, 0, 0, &ErrMsg)) {
139  errs() << "Error: " << ErrMsg << "\n";
140  return true;
141  }
142  sys::fs::remove(Filename);
143  errs() << " done. \n";
144  } else {
145  sys::ExecuteNoWait(ExecPath, args, None, {}, 0, &ErrMsg);
146  errs() << "Remember to erase graph file: " << Filename << "\n";
147  }
148  return false;
149 }
150 
151 namespace {
152 
153 struct GraphSession {
154  std::string LogBuffer;
155 
156  bool TryFindProgram(StringRef Names, std::string &ProgramPath) {
157  raw_string_ostream Log(LogBuffer);
159  Names.split(parts, '|');
160  for (auto Name : parts) {
162  ProgramPath = *P;
163  return true;
164  }
165  Log << " Tried '" << Name << "'\n";
166  }
167  return false;
168  }
169 };
170 
171 } // end anonymous namespace
172 
173 static const char *getProgramName(GraphProgram::Name program) {
174  switch (program) {
175  case GraphProgram::DOT:
176  return "dot";
177  case GraphProgram::FDP:
178  return "fdp";
179  case GraphProgram::NEATO:
180  return "neato";
181  case GraphProgram::TWOPI:
182  return "twopi";
183  case GraphProgram::CIRCO:
184  return "circo";
185  }
186  llvm_unreachable("bad kind");
187 }
188 
189 bool llvm::DisplayGraph(StringRef FilenameRef, bool wait,
190  GraphProgram::Name program) {
191  std::string Filename = std::string(FilenameRef);
192  std::string ErrMsg;
193  std::string ViewerPath;
194  GraphSession S;
195 
196 #ifdef __APPLE__
197  wait &= !*ViewBackground;
198  if (S.TryFindProgram("open", ViewerPath)) {
199  std::vector<StringRef> args;
200  args.push_back(ViewerPath);
201  if (wait)
202  args.push_back("-W");
203  args.push_back(Filename);
204  errs() << "Trying 'open' program... ";
205  if (!ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg))
206  return false;
207  }
208 #endif
209  if (S.TryFindProgram("xdg-open", ViewerPath)) {
210  std::vector<StringRef> args;
211  args.push_back(ViewerPath);
212  args.push_back(Filename);
213  errs() << "Trying 'xdg-open' program... ";
214  if (!ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg))
215  return false;
216  }
217 
218  // Graphviz
219  if (S.TryFindProgram("Graphviz", ViewerPath)) {
220  std::vector<StringRef> args;
221  args.push_back(ViewerPath);
222  args.push_back(Filename);
223 
224  errs() << "Running 'Graphviz' program... ";
225  return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg);
226  }
227 
228  // xdot
229  if (S.TryFindProgram("xdot|xdot.py", ViewerPath)) {
230  std::vector<StringRef> args;
231  args.push_back(ViewerPath);
232  args.push_back(Filename);
233 
234  args.push_back("-f");
235  args.push_back(getProgramName(program));
236 
237  errs() << "Running 'xdot.py' program... ";
238  return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg);
239  }
240 
241  enum ViewerKind {
242  VK_None,
243  VK_OSXOpen,
244  VK_XDGOpen,
245  VK_Ghostview,
246  VK_CmdStart
247  };
248  ViewerKind Viewer = VK_None;
249 #ifdef __APPLE__
250  if (!Viewer && S.TryFindProgram("open", ViewerPath))
251  Viewer = VK_OSXOpen;
252 #endif
253  if (!Viewer && S.TryFindProgram("gv", ViewerPath))
254  Viewer = VK_Ghostview;
255  if (!Viewer && S.TryFindProgram("xdg-open", ViewerPath))
256  Viewer = VK_XDGOpen;
257 #ifdef _WIN32
258  if (!Viewer && S.TryFindProgram("cmd", ViewerPath)) {
259  Viewer = VK_CmdStart;
260  }
261 #endif
262 
263  // PostScript or PDF graph generator + PostScript/PDF viewer
264  std::string GeneratorPath;
265  if (Viewer &&
266  (S.TryFindProgram(getProgramName(program), GeneratorPath) ||
267  S.TryFindProgram("dot|fdp|neato|twopi|circo", GeneratorPath))) {
268  std::string OutputFilename =
269  Filename + (Viewer == VK_CmdStart ? ".pdf" : ".ps");
270 
271  std::vector<StringRef> args;
272  args.push_back(GeneratorPath);
273  if (Viewer == VK_CmdStart)
274  args.push_back("-Tpdf");
275  else
276  args.push_back("-Tps");
277  args.push_back("-Nfontname=Courier");
278  args.push_back("-Gsize=7.5,10");
279  args.push_back(Filename);
280  args.push_back("-o");
281  args.push_back(OutputFilename);
282 
283  errs() << "Running '" << GeneratorPath << "' program... ";
284 
285  if (ExecGraphViewer(GeneratorPath, args, Filename, true, ErrMsg))
286  return true;
287 
288  // The lifetime of StartArg must include the call of ExecGraphViewer
289  // because the args are passed as vector of char*.
290  std::string StartArg;
291 
292  args.clear();
293  args.push_back(ViewerPath);
294  switch (Viewer) {
295  case VK_OSXOpen:
296  args.push_back("-W");
297  args.push_back(OutputFilename);
298  break;
299  case VK_XDGOpen:
300  wait = false;
301  args.push_back(OutputFilename);
302  break;
303  case VK_Ghostview:
304  args.push_back("--spartan");
305  args.push_back(OutputFilename);
306  break;
307  case VK_CmdStart:
308  args.push_back("/S");
309  args.push_back("/C");
310  StartArg =
311  (StringRef("start ") + (wait ? "/WAIT " : "") + OutputFilename).str();
312  args.push_back(StartArg);
313  break;
314  case VK_None:
315  llvm_unreachable("Invalid viewer");
316  }
317 
318  ErrMsg.clear();
319  return ExecGraphViewer(ViewerPath, args, OutputFilename, wait, ErrMsg);
320  }
321 
322  // dotty
323  if (S.TryFindProgram("dotty", ViewerPath)) {
324  std::vector<StringRef> args;
325  args.push_back(ViewerPath);
326  args.push_back(Filename);
327 
328 // Dotty spawns another app and doesn't wait until it returns
329 #ifdef _WIN32
330  wait = false;
331 #endif
332  errs() << "Running 'dotty' program... ";
333  return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg);
334  }
335 
336  errs() << "Error: Couldn't find a usable graph viewer program:\n";
337  errs() << S.LogBuffer << "\n";
338  return true;
339 }
i
i
Definition: README.txt:29
llvm::sys::findProgramByName
ErrorOr< std::string > findProgramByName(StringRef Name, ArrayRef< StringRef > Paths={})
Find the first executable file Name in Paths.
llvm::GraphProgram::TWOPI
@ TWOPI
Definition: GraphWriter.h:56
ExecGraphViewer
static bool ExecGraphViewer(StringRef ExecPath, std::vector< StringRef > &args, StringRef Filename, bool wait, std::string &ErrMsg)
Definition: GraphWriter.cpp:134
llvm
This file implements support for optimizing divisions by a constant.
Definition: AllocatorList.h:23
llvm::sys::ExecuteNoWait
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, BitVector *AffinityMask=nullptr)
Similar to ExecuteAndWait, but returns immediately.
Definition: Program.cpp:57
FileSystem.h
StringRef.h
P
This currently compiles esp xmm0 movsd esp eax eax esp ret We should use not the dag combiner This is because dagcombine2 needs to be able to see through the X86ISD::Wrapper which DAGCombine can t really do The code for turning x load into a single vector load is target independent and should be moved to the dag combiner The code for turning x load into a vector load can only handle a direct load from a global or a direct load from the stack It should be generalized to handle any load from P
Definition: README-SSE.txt:411
llvm::createGraphFilename
std::string createGraphFilename(const Twine &Name, int &FD)
Definition: GraphWriter.cpp:111
getProgramName
static const char * getProgramName(GraphProgram::Name program)
Definition: GraphWriter.cpp:173
llvm::raw_string_ostream
A raw_ostream that writes to an std::string.
Definition: raw_ostream.h:625
llvm::SmallVector
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1168
llvm::DOT::EscapeString
std::string EscapeString(const std::string &Label)
Definition: GraphWriter.cpp:51
ErrorHandling.h
llvm::GraphProgram::CIRCO
@ CIRCO
Definition: GraphWriter.h:57
llvm::cl::Hidden
@ Hidden
Definition: CommandLine.h:143
DebugOptions.h
llvm::errs
raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
Definition: raw_ostream.cpp:893
llvm::initGraphWriterOptions
void initGraphWriterOptions()
Definition: GraphWriter.cpp:48
replace
static void replace(Module &M, GlobalVariable *Old, GlobalVariable *New)
Definition: ConstantMerge.cpp:116
CommandLine.h
llvm::DOT::getColorString
StringRef getColorString(unsigned NodeNumber)
Get a color string for this node number.
Definition: GraphWriter.cpp:86
SmallString.h
llvm::StringRef::split
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:749
llvm::ManagedStatic
ManagedStatic - This transparently changes the behavior of global statics to be lazily constructed on...
Definition: ManagedStatic.h:83
llvm::GraphProgram::DOT
@ DOT
Definition: GraphWriter.h:53
llvm::None
const NoneType None
Definition: None.h:23
llvm::SmallString< 128 >
llvm::cl::opt< bool >
llvm::GraphProgram::FDP
@ FDP
Definition: GraphWriter.h:54
ErrorOr.h
llvm::DisplayGraph
bool DisplayGraph(StringRef Filename, bool wait=true, GraphProgram::Name program=GraphProgram::DOT)
Definition: GraphWriter.cpp:189
call
S is passed via registers r2 But gcc stores them to the and then reload them to and r3 before issuing the call(r0 contains the address of the format string)
Definition: README.txt:190
llvm::sys::fs::remove
std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
llvm::sys::fs::createTemporaryFile
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:856
replaceIllegalFilenameChars
static std::string replaceIllegalFilenameChars(std::string Filename, const char ReplacementChar)
Definition: GraphWriter.cpp:95
llvm::StringRef
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:58
llvm_unreachable
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
Definition: ErrorHandling.h:134
Compiler.h
S
add sub stmia L5 ldr r0 bl L_printf $stub Instead of a and a wouldn t it be better to do three moves *Return an aggregate type is even return S
Definition: README.txt:210
LLVM_FALLTHROUGH
#define LLVM_FALLTHROUGH
LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements.
Definition: Compiler.h:286
llvm::Twine
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:83
llvm::GraphProgram::Name
Name
Definition: GraphWriter.h:52
GraphWriter.h
llvm::SmallString::str
StringRef str() const
Explicit conversion to StringRef.
Definition: SmallString.h:259
SmallVector.h
llvm::GraphProgram::NEATO
@ NEATO
Definition: GraphWriter.h:55
OutputFilename
static cl::opt< std::string > OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), cl::init("-"))
N
#define N
Program.h
llvm::sys::ExecuteAndWait
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, Optional< ProcessStatistics > *ProcStat=nullptr, BitVector *AffinityMask=nullptr)
This function executes the program using the arguments provided.
Definition: Program.cpp:32
llvm::ErrorOr
Represents either an error or a value T.
Definition: ErrorOr.h:56
llvm::cl::desc
Definition: CommandLine.h:412
raw_ostream.h