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