1 //===- unittest/Support/RemarksLinkingTest.cpp - Linking tests ------------===//
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 #include "llvm/Bitcode/BitcodeAnalyzer.h"
10 #include "llvm/Remarks/RemarkLinker.h"
11 #include "llvm/Remarks/RemarkSerializer.h"
12 #include "llvm/Support/raw_ostream.h"
13 #include "gtest/gtest.h"
14 #include <string>
15 
16 using namespace llvm;
17 
serializeAndCheck(remarks::RemarkLinker & RL,remarks::Format OutputFormat,StringRef ExpectedOutput)18 static void serializeAndCheck(remarks::RemarkLinker &RL,
19                               remarks::Format OutputFormat,
20                               StringRef ExpectedOutput) {
21   // 1. Create a serializer.
22   // 2. Serialize all the remarks from the linker.
23   // 3. Check that it matches the output.
24   std::string Buf;
25   raw_string_ostream OS(Buf);
26   Error E = RL.serialize(OS, OutputFormat);
27   EXPECT_FALSE(static_cast<bool>(E));
28 
29   // For bitstream, run it through the analyzer.
30   if (OutputFormat == remarks::Format::Bitstream) {
31     std::string AnalyzeBuf;
32     raw_string_ostream AnalyzeOS(AnalyzeBuf);
33     BCDumpOptions O(AnalyzeOS);
34     O.ShowBinaryBlobs = true;
35     BitcodeAnalyzer BA(OS.str());
36     EXPECT_FALSE(BA.analyze(O)); // Expect no errors.
37     EXPECT_EQ(AnalyzeOS.str(), ExpectedOutput);
38   } else {
39     EXPECT_EQ(OS.str(), ExpectedOutput);
40   }
41 }
42 
check(remarks::Format InputFormat,StringRef Input,remarks::Format OutputFormat,StringRef ExpectedOutput)43 static void check(remarks::Format InputFormat, StringRef Input,
44                   remarks::Format OutputFormat, StringRef ExpectedOutput) {
45   remarks::RemarkLinker RL;
46   EXPECT_FALSE(RL.link(Input, InputFormat));
47   serializeAndCheck(RL, OutputFormat, ExpectedOutput);
48 }
49 
check(remarks::Format InputFormat,StringRef Input,remarks::Format InputFormat2,StringRef Input2,remarks::Format OutputFormat,StringRef ExpectedOutput)50 static void check(remarks::Format InputFormat, StringRef Input,
51                   remarks::Format InputFormat2, StringRef Input2,
52                   remarks::Format OutputFormat, StringRef ExpectedOutput) {
53   remarks::RemarkLinker RL;
54   EXPECT_FALSE(RL.link(Input, InputFormat));
55   EXPECT_FALSE(RL.link(Input2, InputFormat2));
56   serializeAndCheck(RL, OutputFormat, ExpectedOutput);
57 }
58 
TEST(Remarks,LinkingGoodYAML)59 TEST(Remarks, LinkingGoodYAML) {
60   // One YAML remark.
61   check(remarks::Format::YAML,
62         "--- !Missed\n"
63         "Pass:            inline\n"
64         "Name:            NoDefinition\n"
65         "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
66         "Function:        foo\n"
67         "...\n",
68         remarks::Format::YAML,
69         "--- !Missed\n"
70         "Pass:            inline\n"
71         "Name:            NoDefinition\n"
72         "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
73         "Function:        foo\n"
74         "...\n");
75 
76   // Check that we don't keep remarks without debug locations.
77   check(remarks::Format::YAML,
78         "--- !Missed\n"
79         "Pass:            inline\n"
80         "Name:            NoDefinition\n"
81         "Function:        foo\n"
82         "...\n",
83         remarks::Format::YAML, "");
84 
85   // Check that we deduplicate remarks.
86   check(remarks::Format::YAML,
87         "--- !Missed\n"
88         "Pass:            inline\n"
89         "Name:            NoDefinition\n"
90         "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
91         "Function:        foo\n"
92         "...\n"
93         "--- !Missed\n"
94         "Pass:            inline\n"
95         "Name:            NoDefinition\n"
96         "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
97         "Function:        foo\n"
98         "...\n",
99         remarks::Format::YAML,
100         "--- !Missed\n"
101         "Pass:            inline\n"
102         "Name:            NoDefinition\n"
103         "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
104         "Function:        foo\n"
105         "...\n");
106 }
107 
TEST(Remarks,LinkingGoodBitstream)108 TEST(Remarks, LinkingGoodBitstream) {
109   // One YAML remark.
110   check(remarks::Format::YAML,
111         "--- !Missed\n"
112         "Pass:            inline\n"
113         "Name:            NoDefinition\n"
114         "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
115         "Function:        foo\n"
116         "...\n",
117         remarks::Format::Bitstream,
118         "<BLOCKINFO_BLOCK/>\n"
119         "<Meta BlockID=8 NumWords=12 BlockCodeSize=3>\n"
120         "  <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
121         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
122         "  <String table codeid=3 abbrevid=6/> blob data = "
123         "'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n"
124         "</Meta>\n"
125         "<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
126         "  <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
127         "  <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n"
128         "</Remark>\n");
129 
130   // Check that we deduplicate remarks.
131   check(remarks::Format::YAML,
132         "--- !Missed\n"
133         "Pass:            inline\n"
134         "Name:            NoDefinition\n"
135         "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
136         "Function:        foo\n"
137         "...\n"
138         "--- !Missed\n"
139         "Pass:            inline\n"
140         "Name:            NoDefinition\n"
141         "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
142         "Function:        foo\n"
143         "...\n",
144         remarks::Format::Bitstream,
145         "<BLOCKINFO_BLOCK/>\n"
146         "<Meta BlockID=8 NumWords=12 BlockCodeSize=3>\n"
147         "  <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
148         "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
149         "  <String table codeid=3 abbrevid=6/> blob data = "
150         "'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n"
151         "</Meta>\n"
152         "<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
153         "  <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
154         "  <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n"
155         "</Remark>\n");
156 }
157 
TEST(Remarks,LinkingGoodStrTab)158 TEST(Remarks, LinkingGoodStrTab) {
159   // Check that remarks from different entries use the same strtab.
160   check(remarks::Format::YAML,
161         "--- !Missed\n"
162         "Pass:            inline\n"
163         "Name:            NoDefinition\n"
164         "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
165         "Function:        foo\n"
166         "...\n",
167         remarks::Format::YAML,
168         "--- !Passed\n"
169         "Pass:            inline\n"
170         "Name:            Ok\n"
171         "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
172         "Function:        foo\n"
173         "...\n",
174         remarks::Format::YAMLStrTab,
175         StringRef("REMARKS\0\0\0\0\0\0\0\0\0\x22\0\0\0\0\0\0\0"
176                   "inline\0NoDefinition\0foo\0file.c\0Ok\0"
177                   "--- !Passed\n"
178                   "Pass:            0\n"
179                   "Name:            4\n"
180                   "DebugLoc:        { File: 3, Line: 3, Column: 12 }\n"
181                   "Function:        2\n"
182                   "...\n"
183                   "--- !Missed\n"
184                   "Pass:            0\n"
185                   "Name:            1\n"
186                   "DebugLoc:        { File: 3, Line: 3, Column: 12 }\n"
187                   "Function:        2\n"
188                   "...\n",
189                   304));
190 }
191 
192 // Check that we propagate parsing errors.
TEST(Remarks,LinkingError)193 TEST(Remarks, LinkingError) {
194   remarks::RemarkLinker RL;
195   {
196     Error E = RL.link("badyaml", remarks::Format::YAML);
197     EXPECT_TRUE(static_cast<bool>(E));
198     EXPECT_EQ(toString(std::move(E)),
199               "YAML:1:1: error: document root is not of mapping type.\n"
200               "\n"
201               "badyaml\n"
202               "^~~~~~~\n"
203               "\n");
204   }
205 
206   {
207     // Check that the prepend path is propagated and fails with the full path.
208     RL.setExternalFilePrependPath("/baddir/");
209     Error E = RL.link(
210         StringRef("REMARKS\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0badfile.opt.yaml",
211                   40),
212         remarks::Format::YAMLStrTab);
213     EXPECT_TRUE(static_cast<bool>(E));
214     std::string ErrorMessage = toString(std::move(E));
215     EXPECT_EQ(StringRef(ErrorMessage).lower(),
216               StringRef("'/baddir/badfile.opt.yaml': No such file or directory")
217                   .lower());
218   }
219 }
220