Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/tools/driver/cc1gen_reproducer_main.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- cc1gen_reproducer_main.cpp - Clang reproducer generator  ----------===//
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 is the entry point to the clang -cc1gen-reproducer functionality, which
10
// generates reproducers for invocations for clang-based tools.
11
//
12
//===----------------------------------------------------------------------===//
13
14
#include "clang/Basic/Diagnostic.h"
15
#include "clang/Basic/LLVM.h"
16
#include "clang/Driver/Compilation.h"
17
#include "clang/Driver/Driver.h"
18
#include "llvm/ADT/ArrayRef.h"
19
#include "llvm/ADT/STLExtras.h"
20
#include "llvm/Support/FileSystem.h"
21
#include "llvm/Support/TargetSelect.h"
22
#include "llvm/Support/VirtualFileSystem.h"
23
#include "llvm/Support/YAMLTraits.h"
24
#include "llvm/Support/raw_ostream.h"
25
26
using namespace clang;
27
28
namespace {
29
30
struct UnsavedFileHash {
31
  std::string Name;
32
  std::string MD5;
33
};
34
35
struct ClangInvocationInfo {
36
  std::string Toolchain;
37
  std::string LibclangOperation;
38
  std::string LibclangOptions;
39
  std::vector<std::string> Arguments;
40
  std::vector<std::string> InvocationArguments;
41
  std::vector<UnsavedFileHash> UnsavedFileHashes;
42
  bool Dump = false;
43
};
44
45
} // end anonymous namespace
46
47
LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash)
48
49
namespace llvm {
50
namespace yaml {
51
52
template <> struct MappingTraits<UnsavedFileHash> {
53
2
  static void mapping(IO &IO, UnsavedFileHash &Info) {
54
2
    IO.mapRequired("name", Info.Name);
55
2
    IO.mapRequired("md5", Info.MD5);
56
2
  }
57
};
58
59
template <> struct MappingTraits<ClangInvocationInfo> {
60
3
  static void mapping(IO &IO, ClangInvocationInfo &Info) {
61
3
    IO.mapRequired("toolchain", Info.Toolchain);
62
3
    IO.mapOptional("libclang.operation", Info.LibclangOperation);
63
3
    IO.mapOptional("libclang.opts", Info.LibclangOptions);
64
3
    IO.mapRequired("args", Info.Arguments);
65
3
    IO.mapOptional("invocation-args", Info.InvocationArguments);
66
3
    IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes);
67
3
  }
68
};
69
70
} // end namespace yaml
71
} // end namespace llvm
72
73
3
static std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) {
74
3
  std::string Result;
75
3
  llvm::raw_string_ostream OS(Result);
76
3
  OS << '{';
77
3
  bool NeedComma = false;
78
7
  auto EmitKey = [&](StringRef Key) {
79
7
    if (NeedComma)
80
4
      OS << ", ";
81
7
    NeedComma = true;
82
7
    OS << '"' << Key << "\": ";
83
7
  };
84
6
  auto EmitStringKey = [&](StringRef Key, StringRef Value) {
85
6
    if (Value.empty())
86
0
      return;
87
6
    EmitKey(Key);
88
6
    OS << '"' << Value << '"';
89
6
  };
90
3
  EmitStringKey("libclang.operation", Info.LibclangOperation);
91
3
  EmitStringKey("libclang.opts", Info.LibclangOptions);
92
3
  if (!Info.InvocationArguments.empty()) {
93
1
    EmitKey("invocation-args");
94
1
    OS << '[';
95
1
    for (const auto &Arg : llvm::enumerate(Info.InvocationArguments)) {
96
1
      if (Arg.index())
97
0
        OS << ',';
98
1
      OS << '"' << Arg.value() << '"';
99
1
    }
100
1
    OS << ']';
101
1
  }
102
3
  OS << '}';
103
3
  // FIXME: Compare unsaved file hashes and report mismatch in the reproducer.
104
3
  if (Info.Dump)
105
3
    llvm::outs() << "REPRODUCER METAINFO: " << OS.str() << "\n";
106
3
  return std::move(OS.str());
107
3
}
108
109
/// Generates a reproducer for a set of arguments from a specific invocation.
110
static llvm::Optional<driver::Driver::CompilationDiagnosticReport>
111
generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
112
3
                                         const ClangInvocationInfo &Info) {
113
3
  using namespace driver;
114
3
  auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]);
115
3
116
3
  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions;
117
3
118
3
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
119
3
  DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer());
120
3
  ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
121
3
  Driver TheDriver(Argv[0], llvm::sys::getDefaultTargetTriple(), Diags);
122
3
  TheDriver.setTargetAndMode(TargetAndMode);
123
3
124
3
  std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv));
125
3
  if (C && !C->containsError()) {
126
3
    for (const auto &J : C->getJobs()) {
127
3
      if (const Command *Cmd = dyn_cast<Command>(&J)) {
128
3
        Driver::CompilationDiagnosticReport Report;
129
3
        TheDriver.generateCompilationDiagnostics(
130
3
            *C, *Cmd, generateReproducerMetaInfo(Info), &Report);
131
3
        return Report;
132
3
      }
133
3
    }
134
3
  }
135
3
136
3
  
return None0
;
137
3
}
138
139
std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes);
140
141
static void printReproducerInformation(
142
    llvm::raw_ostream &OS, const ClangInvocationInfo &Info,
143
3
    const driver::Driver::CompilationDiagnosticReport &Report) {
144
3
  OS << "REPRODUCER:\n";
145
3
  OS << "{\n";
146
3
  OS << R"("files":[)";
147
6
  for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) {
148
6
    if (File.index())
149
3
      OS << ',';
150
6
    OS << '"' << File.value() << '"';
151
6
  }
152
3
  OS << "]\n}\n";
153
3
}
154
155
int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0,
156
3
                           void *MainAddr) {
157
3
  if (Argv.size() < 1) {
158
0
    llvm::errs() << "error: missing invocation file\n";
159
0
    return 1;
160
0
  }
161
3
  // Parse the invocation descriptor.
162
3
  StringRef Input = Argv[0];
163
3
  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
164
3
      llvm::MemoryBuffer::getFile(Input);
165
3
  if (!Buffer) {
166
0
    llvm::errs() << "error: failed to read " << Input << ": "
167
0
                 << Buffer.getError().message() << "\n";
168
0
    return 1;
169
0
  }
170
3
  llvm::yaml::Input YAML(Buffer.get()->getBuffer());
171
3
  ClangInvocationInfo InvocationInfo;
172
3
  YAML >> InvocationInfo;
173
3
  if (Argv.size() > 1 && Argv[1] == StringRef("-v"))
174
3
    InvocationInfo.Dump = true;
175
3
176
3
  // Create an invocation that will produce the reproducer.
177
3
  std::vector<const char *> DriverArgs;
178
3
  for (const auto &Arg : InvocationInfo.Arguments)
179
18
    DriverArgs.push_back(Arg.c_str());
180
3
  std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true);
181
3
  DriverArgs[0] = Path.c_str();
182
3
  llvm::Optional<driver::Driver::CompilationDiagnosticReport> Report =
183
3
      generateReproducerForInvocationArguments(DriverArgs, InvocationInfo);
184
3
185
3
  // Emit the information about the reproduce files to stdout.
186
3
  int Result = 1;
187
3
  if (Report) {
188
3
    printReproducerInformation(llvm::outs(), InvocationInfo, *Report);
189
3
    Result = 0;
190
3
  }
191
3
192
3
  // Remove the input file.
193
3
  llvm::sys::fs::remove(Input);
194
3
  return Result;
195
3
}