LLVM  15.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"
21 #include "llvm/Support/Compiler.h"
23 #include "llvm/Support/ErrorOr.h"
25 #include "llvm/Support/Path.h"
26 #include "llvm/Support/Program.h"
28 
29 #ifdef __APPLE__
31 #endif
32 
33 #include <string>
34 #include <system_error>
35 #include <vector>
36 
37 using namespace llvm;
38 
39 #ifdef __APPLE__
40 namespace {
41 struct CreateViewBackground {
42  static void *call() {
43  return new cl::opt<bool>("view-background", cl::Hidden,
44  cl::desc("Execute graph viewer in the background. "
45  "Creates tmp file litter."));
46  }
47 };
48 } // namespace
49 static ManagedStatic<cl::opt<bool>, CreateViewBackground> ViewBackground;
50 void llvm::initGraphWriterOptions() { *ViewBackground; }
51 #else
53 #endif
54 
55 std::string llvm::DOT::EscapeString(const std::string &Label) {
56  std::string Str(Label);
57  for (unsigned i = 0; i != Str.length(); ++i)
58  switch (Str[i]) {
59  case '\n':
60  Str.insert(Str.begin()+i, '\\'); // Escape character...
61  ++i;
62  Str[i] = 'n';
63  break;
64  case '\t':
65  Str.insert(Str.begin()+i, ' '); // Convert to two spaces
66  ++i;
67  Str[i] = ' ';
68  break;
69  case '\\':
70  if (i+1 != Str.length())
71  switch (Str[i+1]) {
72  case 'l': continue; // don't disturb \l
73  case '|': case '{': case '}':
74  Str.erase(Str.begin()+i); continue;
75  default: break;
76  }
78  case '{': case '}':
79  case '<': case '>':
80  case '|': case '"':
81  Str.insert(Str.begin()+i, '\\'); // Escape character...
82  ++i; // don't infinite loop
83  break;
84  }
85  return Str;
86 }
87 
88 /// Get a color string for this node number. Simply round-robin selects
89 /// from a reasonable number of colors.
90 StringRef llvm::DOT::getColorString(unsigned ColorNumber) {
91  static const int NumColors = 20;
92  static const char* Colors[NumColors] = {
93  "aaaaaa", "aa0000", "00aa00", "aa5500", "0055ff", "aa00aa", "00aaaa",
94  "555555", "ff5555", "55ff55", "ffff55", "5555ff", "ff55ff", "55ffff",
95  "ffaaaa", "aaffaa", "ffffaa", "aaaaff", "ffaaff", "aaffff"};
96  return Colors[ColorNumber % NumColors];
97 }
98 
99 static std::string replaceIllegalFilenameChars(std::string Filename,
100  const char ReplacementChar) {
101  std::string IllegalChars =
102  is_style_windows(sys::path::Style::native) ? "\\/:?\"<>|" : "/";
103 
104  for (char IllegalChar : IllegalChars) {
105  std::replace(Filename.begin(), Filename.end(), IllegalChar,
106  ReplacementChar);
107  }
108 
109  return Filename;
110 }
111 
112 std::string llvm::createGraphFilename(const Twine &Name, int &FD) {
113  FD = -1;
114  SmallString<128> Filename;
115 
116  // Windows can't always handle long paths, so limit the length of the name.
117  std::string N = Name.str();
118  N = N.substr(0, std::min<std::size_t>(N.size(), 140));
119 
120  // Replace illegal characters in graph Filename with '_' if needed
121  std::string CleansedName = replaceIllegalFilenameChars(N, '_');
122 
123  std::error_code EC =
124  sys::fs::createTemporaryFile(CleansedName, "dot", FD, Filename);
125  if (EC) {
126  errs() << "Error: " << EC.message() << "\n";
127  return "";
128  }
129 
130  errs() << "Writing '" << Filename << "'... ";
131  return std::string(Filename.str());
132 }
133 
134 // Execute the graph viewer. Return true if there were errors.
135 static bool ExecGraphViewer(StringRef ExecPath, std::vector<StringRef> &args,
136  StringRef Filename, bool wait,
137  std::string &ErrMsg) {
138  if (wait) {
139  if (sys::ExecuteAndWait(ExecPath, args, None, {}, 0, 0, &ErrMsg)) {
140  errs() << "Error: " << ErrMsg << "\n";
141  return true;
142  }
143  sys::fs::remove(Filename);
144  errs() << " done. \n";
145  } else {
146  sys::ExecuteNoWait(ExecPath, args, None, {}, 0, &ErrMsg);
147  errs() << "Remember to erase graph file: " << Filename << "\n";
148  }
149  return false;
150 }
151 
152 namespace {
153 
154 struct GraphSession {
155  std::string LogBuffer;
156 
157  bool TryFindProgram(StringRef Names, std::string &ProgramPath) {
158  raw_string_ostream Log(LogBuffer);
160  Names.split(parts, '|');
161  for (auto Name : parts) {
163  ProgramPath = *P;
164  return true;
165  }
166  Log << " Tried '" << Name << "'\n";
167  }
168  return false;
169  }
170 };
171 
172 } // end anonymous namespace
173 
174 static const char *getProgramName(GraphProgram::Name program) {
175  switch (program) {
176  case GraphProgram::DOT:
177  return "dot";
178  case GraphProgram::FDP:
179  return "fdp";
180  case GraphProgram::NEATO:
181  return "neato";
182  case GraphProgram::TWOPI:
183  return "twopi";
184  case GraphProgram::CIRCO:
185  return "circo";
186  }
187  llvm_unreachable("bad kind");
188 }
189 
190 bool llvm::DisplayGraph(StringRef FilenameRef, bool wait,
191  GraphProgram::Name program) {
192  std::string Filename = std::string(FilenameRef);
193  std::string ErrMsg;
194  std::string ViewerPath;
195  GraphSession S;
196 
197 #ifdef __APPLE__
198  wait &= !*ViewBackground;
199  if (S.TryFindProgram("open", ViewerPath)) {
200  std::vector<StringRef> args;
201  args.push_back(ViewerPath);
202  if (wait)
203  args.push_back("-W");
204  args.push_back(Filename);
205  errs() << "Trying 'open' program... ";
206  if (!ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg))
207  return false;
208  }
209 #endif
210  if (S.TryFindProgram("xdg-open", ViewerPath)) {
211  std::vector<StringRef> args;
212  args.push_back(ViewerPath);
213  args.push_back(Filename);
214  errs() << "Trying 'xdg-open' program... ";
215  if (!ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg))
216  return false;
217  }
218 
219  // Graphviz
220  if (S.TryFindProgram("Graphviz", ViewerPath)) {
221  std::vector<StringRef> args;
222  args.push_back(ViewerPath);
223  args.push_back(Filename);
224 
225  errs() << "Running 'Graphviz' program... ";
226  return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg);
227  }
228 
229  // xdot
230  if (S.TryFindProgram("xdot|xdot.py", ViewerPath)) {
231  std::vector<StringRef> args;
232  args.push_back(ViewerPath);
233  args.push_back(Filename);
234 
235  args.push_back("-f");
236  args.push_back(getProgramName(program));
237 
238  errs() << "Running 'xdot.py' program... ";
239  return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg);
240  }
241 
242  enum ViewerKind {
243  VK_None,
244  VK_OSXOpen,
245  VK_XDGOpen,
246  VK_Ghostview,
247  VK_CmdStart
248  };
249  ViewerKind Viewer = VK_None;
250 #ifdef __APPLE__
251  if (!Viewer && S.TryFindProgram("open", ViewerPath))
252  Viewer = VK_OSXOpen;
253 #endif
254  if (!Viewer && S.TryFindProgram("gv", ViewerPath))
255  Viewer = VK_Ghostview;
256  if (!Viewer && S.TryFindProgram("xdg-open", ViewerPath))
257  Viewer = VK_XDGOpen;
258 #ifdef _WIN32
259  if (!Viewer && S.TryFindProgram("cmd", ViewerPath)) {
260  Viewer = VK_CmdStart;
261  }
262 #endif
263 
264  // PostScript or PDF graph generator + PostScript/PDF viewer
265  std::string GeneratorPath;
266  if (Viewer &&
267  (S.TryFindProgram(getProgramName(program), GeneratorPath) ||
268  S.TryFindProgram("dot|fdp|neato|twopi|circo", GeneratorPath))) {
269  std::string OutputFilename =
270  Filename + (Viewer == VK_CmdStart ? ".pdf" : ".ps");
271 
272  std::vector<StringRef> args;
273  args.push_back(GeneratorPath);
274  if (Viewer == VK_CmdStart)
275  args.push_back("-Tpdf");
276  else
277  args.push_back("-Tps");
278  args.push_back("-Nfontname=Courier");
279  args.push_back("-Gsize=7.5,10");
280  args.push_back(Filename);
281  args.push_back("-o");
282  args.push_back(OutputFilename);
283 
284  errs() << "Running '" << GeneratorPath << "' program... ";
285 
286  if (ExecGraphViewer(GeneratorPath, args, Filename, true, ErrMsg))
287  return true;
288 
289  // The lifetime of StartArg must include the call of ExecGraphViewer
290  // because the args are passed as vector of char*.
291  std::string StartArg;
292 
293  args.clear();
294  args.push_back(ViewerPath);
295  switch (Viewer) {
296  case VK_OSXOpen:
297  args.push_back("-W");
298  args.push_back(OutputFilename);
299  break;
300  case VK_XDGOpen:
301  wait = false;
302  args.push_back(OutputFilename);
303  break;
304  case VK_Ghostview:
305  args.push_back("--spartan");
306  args.push_back(OutputFilename);
307  break;
308  case VK_CmdStart:
309  args.push_back("/S");
310  args.push_back("/C");
311  StartArg =
312  (StringRef("start ") + (wait ? "/WAIT " : "") + OutputFilename).str();
313  args.push_back(StartArg);
314  break;
315  case VK_None:
316  llvm_unreachable("Invalid viewer");
317  }
318 
319  ErrMsg.clear();
320  return ExecGraphViewer(ViewerPath, args, OutputFilename, wait, ErrMsg);
321  }
322 
323  // dotty
324  if (S.TryFindProgram("dotty", ViewerPath)) {
325  std::vector<StringRef> args;
326  args.push_back(ViewerPath);
327  args.push_back(Filename);
328 
329 // Dotty spawns another app and doesn't wait until it returns
330 #ifdef _WIN32
331  wait = false;
332 #endif
333  errs() << "Running 'dotty' program... ";
334  return ExecGraphViewer(ViewerPath, args, Filename, wait, ErrMsg);
335  }
336 
337  errs() << "Error: Couldn't find a usable graph viewer program:\n";
338  errs() << S.LogBuffer << "\n";
339  return true;
340 }
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:54
ExecGraphViewer
static bool ExecGraphViewer(StringRef ExecPath, std::vector< StringRef > &args, StringRef Filename, bool wait, std::string &ErrMsg)
Definition: GraphWriter.cpp:135
llvm
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:17
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:56
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:112
getProgramName
static const char * getProgramName(GraphProgram::Name program)
Definition: GraphWriter.cpp:174
llvm::raw_string_ostream
A raw_ostream that writes to an std::string.
Definition: raw_ostream.h:632
llvm::SmallVector
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1185
Path.h
llvm::DOT::EscapeString
std::string EscapeString(const std::string &Label)
Definition: GraphWriter.cpp:55
ErrorHandling.h
llvm::GraphProgram::CIRCO
@ CIRCO
Definition: GraphWriter.h:55
llvm::cl::Hidden
@ Hidden
Definition: CommandLine.h:139
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:52
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:90
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:51
llvm::None
const NoneType None
Definition: None.h:24
llvm::SmallString< 128 >
llvm::cl::opt< bool >
llvm::GraphProgram::FDP
@ FDP
Definition: GraphWriter.h:52
ErrorOr.h
llvm::DisplayGraph
bool DisplayGraph(StringRef Filename, bool wait=true, GraphProgram::Name program=GraphProgram::DOT)
Definition: GraphWriter.cpp:190
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::path::Style::native
@ native
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:859
replaceIllegalFilenameChars
static std::string replaceIllegalFilenameChars(std::string Filename, const char ReplacementChar)
Definition: GraphWriter.cpp:99
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:143
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:280
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:50
GraphWriter.h
llvm::SmallString::str
StringRef str() const
Explicit conversion to StringRef.
Definition: SmallString.h:260
SmallVector.h
llvm::GraphProgram::NEATO
@ NEATO
Definition: GraphWriter.h:53
OutputFilename
static cl::opt< std::string > OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), cl::init("-"))
llvm::sys::path::is_style_windows
constexpr bool is_style_windows(Style S)
Check if S uses Windows path rules.
Definition: Path.h:49
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:31
llvm::ErrorOr
Represents either an error or a value T.
Definition: ErrorOr.h:56
llvm::cl::desc
Definition: CommandLine.h:405
raw_ostream.h