1 //===- llvm-profdata.cpp - LLVM profile data tool -------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // llvm-profdata merges .profdata files.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/Support/CommandLine.h"
16 #include "llvm/Support/LineIterator.h"
17 #include "llvm/Support/ManagedStatic.h"
18 #include "llvm/Support/MemoryBuffer.h"
19 #include "llvm/Support/PrettyStackTrace.h"
20 #include "llvm/Support/Signals.h"
21 #include "llvm/Support/raw_ostream.h"
22 
23 using namespace llvm;
24 
25 static void exitWithError(const std::string &Message,
26                           const std::string &Filename, int64_t Line = -1) {
27   errs() << "error: " << Filename;
28   if (Line >= 0)
29     errs() << ":" << Line;
30   errs() << ": " << Message << "\n";
31   ::exit(1);
32 }
33 
34 //===----------------------------------------------------------------------===//
35 int merge_main(int argc, const char *argv[]) {
36   cl::opt<std::string> Filename1(cl::Positional, cl::Required,
37                                  cl::desc("file1"));
38   cl::opt<std::string> Filename2(cl::Positional, cl::Required,
39                                  cl::desc("file2"));
40 
41   cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
42                                       cl::init("-"),
43                                       cl::desc("Output file"));
44   cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
45                                  cl::aliasopt(OutputFilename));
46 
47   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
48 
49   std::unique_ptr<MemoryBuffer> File1;
50   std::unique_ptr<MemoryBuffer> File2;
51   if (error_code ec = MemoryBuffer::getFile(Filename1, File1))
52     exitWithError(ec.message(), Filename1);
53   if (error_code ec = MemoryBuffer::getFile(Filename2, File2))
54     exitWithError(ec.message(), Filename2);
55 
56   if (OutputFilename.empty())
57     OutputFilename = "-";
58 
59   std::string ErrorInfo;
60   raw_fd_ostream Output(OutputFilename.data(), ErrorInfo, sys::fs::F_Text);
61   if (!ErrorInfo.empty())
62     exitWithError(ErrorInfo, OutputFilename);
63 
64   enum {ReadName, ReadHash, ReadCount, ReadCounters} State = ReadName;
65   uint64_t N1, N2, NumCounters;
66   line_iterator I1(*File1, '#'), I2(*File2, '#');
67   for (; !I1.is_at_end() && !I2.is_at_end(); ++I1, ++I2) {
68     if (I1->empty()) {
69       if (!I2->empty())
70         exitWithError("data mismatch", Filename2, I2.line_number());
71       Output << "\n";
72       continue;
73     }
74     switch (State) {
75     case ReadName:
76       if (*I1 != *I2)
77         exitWithError("function name mismatch", Filename2, I2.line_number());
78       Output << *I1 << "\n";
79       State = ReadHash;
80       break;
81     case ReadHash:
82       if (I1->getAsInteger(10, N1))
83         exitWithError("bad function hash", Filename1, I1.line_number());
84       if (I2->getAsInteger(10, N2))
85         exitWithError("bad function hash", Filename2, I2.line_number());
86       if (N1 != N2)
87         exitWithError("function hash mismatch", Filename2, I2.line_number());
88       Output << N1 << "\n";
89       State = ReadCount;
90       break;
91     case ReadCount:
92       if (I1->getAsInteger(10, N1))
93         exitWithError("bad function count", Filename1, I1.line_number());
94       if (I2->getAsInteger(10, N2))
95         exitWithError("bad function count", Filename2, I2.line_number());
96       if (N1 != N2)
97         exitWithError("function count mismatch", Filename2, I2.line_number());
98       Output << N1 << "\n";
99       NumCounters = N1;
100       State = ReadCounters;
101       break;
102     case ReadCounters:
103       if (I1->getAsInteger(10, N1))
104         exitWithError("invalid counter", Filename1, I1.line_number());
105       if (I2->getAsInteger(10, N2))
106         exitWithError("invalid counter", Filename2, I2.line_number());
107       uint64_t Sum = N1 + N2;
108       if (Sum < N1)
109         exitWithError("counter overflow", Filename2, I2.line_number());
110       Output << N1 + N2 << "\n";
111       if (--NumCounters == 0)
112         State = ReadName;
113       break;
114     }
115   }
116   if (!I1.is_at_end())
117     exitWithError("truncated file", Filename1, I1.line_number());
118   if (!I2.is_at_end())
119     exitWithError("truncated file", Filename2, I2.line_number());
120   if (State != ReadName)
121     exitWithError("truncated file", Filename1, I1.line_number());
122 
123   return 0;
124 }
125 
126 int main(int argc, const char *argv[]) {
127   // Print a stack trace if we signal out.
128   sys::PrintStackTraceOnErrorSignal();
129   PrettyStackTraceProgram X(argc, argv);
130   llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
131 
132   StringRef ProgName(sys::path::filename(argv[0]));
133   if (argc > 1) {
134     int (*func)(int, const char *[]) = 0;
135 
136     if (strcmp(argv[1], "merge") == 0)
137       func = merge_main;
138 
139     if (func) {
140       std::string Invocation(ProgName.str() + " " + argv[1]);
141       argv[1] = Invocation.c_str();
142       return func(argc - 1, argv + 1);
143     }
144 
145     if (strcmp(argv[1], "-h") == 0 ||
146         strcmp(argv[1], "-help") == 0 ||
147         strcmp(argv[1], "--help") == 0) {
148 
149       errs() << "OVERVIEW: LLVM profile data tools\n\n"
150              << "USAGE: " << ProgName << " <command> [args...]\n"
151              << "USAGE: " << ProgName << " <command> -help\n\n"
152              << "Available commands: merge\n";
153       return 0;
154     }
155   }
156 
157   if (argc < 2)
158     errs() << ProgName << ": No command specified!\n";
159   else
160     errs() << ProgName << ": Unknown command!\n";
161 
162   errs() << "USAGE: " << ProgName << " <merge|show|generate> [args...]\n";
163   return 1;
164 }
165