1
+ // ===- Delta.cpp - Delta Debugging Algorithm Implementation ---------------===//
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 contains the implementation for the Delta Debugging Algorithm:
10
+ // it splits a given set of Targets (i.e. Functions, Instructions, BBs, etc.)
11
+ // into chunks and tries to reduce the number chunks that are interesting.
12
+ //
13
+ // ===----------------------------------------------------------------------===//
14
+
15
+ #include " Delta.h"
16
+
17
+ // / Writes IR code to the given Filepath
18
+ static bool writeProgramToFile (StringRef Filepath, int FD, const Module &M) {
19
+ ToolOutputFile Out (Filepath, FD);
20
+ M.print (Out.os (), /* AnnotationWriter=*/ nullptr );
21
+ Out.os ().close ();
22
+
23
+ if (!Out.os ().has_error ()) {
24
+ Out.keep ();
25
+ return false ;
26
+ }
27
+ return true ;
28
+ }
29
+
30
+ // / Creates a temporary (and unique) file inside the tmp folder and writes
31
+ // / the given module IR.
32
+ static SmallString<128 > createTmpFile (Module *M, StringRef TmpDir) {
33
+ SmallString<128 > UniqueFilepath;
34
+ int UniqueFD;
35
+
36
+ std::error_code EC = sys::fs::createUniqueFile (TmpDir + " /tmp-%%%.ll" ,
37
+ UniqueFD, UniqueFilepath);
38
+ if (EC) {
39
+ errs () << " Error making unique filename: " << EC.message () << " !\n " ;
40
+ exit (1 );
41
+ }
42
+
43
+ if (writeProgramToFile (UniqueFilepath, UniqueFD, *M)) {
44
+ errs () << " Error emitting bitcode to file '" << UniqueFilepath << " '!\n " ;
45
+ exit (1 );
46
+ }
47
+ return UniqueFilepath;
48
+ }
49
+
50
+ // / Prints the Chunk Indexes with the following format: [start, end], if
51
+ // / chunk is at minimum size (1), then it just displays [start].
52
+ static void printChunks (std::vector<Chunk> Chunks, bool Oneline = false ) {
53
+ for (auto C : Chunks) {
54
+ if (!Oneline)
55
+ outs () << ' \t ' ;
56
+ outs () << " [" << C.begin ;
57
+ if (C.end - C.begin != 0 )
58
+ outs () << " ," << C.end ;
59
+ outs () << " ]" ;
60
+ if (!Oneline)
61
+ outs () << ' \n ' ;
62
+ }
63
+ }
64
+
65
+ // / Counts the amount of lines for a given file
66
+ static unsigned getLines (StringRef Filepath) {
67
+ unsigned Lines = 0 ;
68
+ std::string CurrLine;
69
+ std::ifstream FileStream (Filepath);
70
+
71
+ while (std::getline (FileStream, CurrLine))
72
+ ++Lines;
73
+
74
+ return Lines;
75
+ }
76
+
77
+ // / Splits Chunks in half and prints them.
78
+ // / If unable to split (when chunk size is 1) returns false.
79
+ static bool increaseGranularity (std::vector<Chunk> &Chunks) {
80
+ outs () << " Increasing granularity..." ;
81
+ std::vector<Chunk> NewChunks;
82
+ bool SplitOne = false ;
83
+
84
+ for (auto &C : Chunks) {
85
+ if (C.end - C.begin == 0 )
86
+ NewChunks.push_back (C);
87
+ else {
88
+ int Half = (C.begin + C.end ) / 2 ;
89
+ NewChunks.push_back ({C.begin , Half});
90
+ NewChunks.push_back ({Half + 1 , C.end });
91
+ SplitOne = true ;
92
+ }
93
+ }
94
+ if (SplitOne) {
95
+ Chunks = NewChunks;
96
+ outs () << " Success! New Chunks:\n " ;
97
+ printChunks (Chunks);
98
+ }
99
+ return SplitOne;
100
+ }
101
+
102
+ void llvm::runDeltaPass (
103
+ TestRunner &Test, int Targets,
104
+ std::function<std::unique_ptr<Module>(std::vector<Chunk>, Module *)>
105
+ ExtractChunksFromModule) {
106
+ if (!Targets)
107
+ return ;
108
+
109
+ std::vector<Chunk> Chunks = {{1 , Targets}};
110
+ std::set<Chunk> UninterestingChunks;
111
+ std::unique_ptr<Module> ReducedProgram;
112
+
113
+ if (!Test.run (Test.getReducedFilepath ()) || !increaseGranularity (Chunks)) {
114
+ outs () << " \n Couldn't reduce\n " ;
115
+ outs () << " ----------------------------\n " ;
116
+ return ;
117
+ }
118
+
119
+ do {
120
+ UninterestingChunks = {};
121
+ for (int I = Chunks.size () - 1 ; I >= 0 ; --I) {
122
+ std::vector<Chunk> CurrentChunks;
123
+
124
+ for (auto C : Chunks)
125
+ if (!UninterestingChunks.count (C) && C != Chunks[I])
126
+ CurrentChunks.push_back (C);
127
+
128
+ if (CurrentChunks.empty ())
129
+ break ;
130
+
131
+ // Generate Module with only Targets inside Current Chunks
132
+ std::unique_ptr<Module> CurrentProgram =
133
+ ExtractChunksFromModule (CurrentChunks, Test.getProgram ());
134
+ // Write Module to tmp file
135
+ SmallString<128 > CurrentFilepath =
136
+ createTmpFile (CurrentProgram.get (), Test.getTmpDir ());
137
+
138
+ outs () << " Testing with: " ;
139
+ printChunks (CurrentChunks, /* Oneline=*/ true );
140
+ outs () << " | " << sys::path::filename (CurrentFilepath);
141
+
142
+ // Current Chunks aren't interesting
143
+ if (!Test.run (CurrentFilepath)) {
144
+ outs () << " \n " ;
145
+ continue ;
146
+ }
147
+ // We only care about interesting chunks if they reduce the testcase
148
+ if (getLines (CurrentFilepath) < getLines (Test.getReducedFilepath ())) {
149
+ UninterestingChunks.insert (Chunks[I]);
150
+ Test.setReducedFilepath (CurrentFilepath);
151
+ ReducedProgram = std::move (CurrentProgram);
152
+ outs () << " **** SUCCESS | lines: " << getLines (CurrentFilepath);
153
+ }
154
+ outs () << " \n " ;
155
+ }
156
+ // Delete uninteresting chunks
157
+ auto UnwantedChunks = Chunks.end ();
158
+ UnwantedChunks = std::remove_if (Chunks.begin (), Chunks.end (),
159
+ [UninterestingChunks](const Chunk &C) {
160
+ return UninterestingChunks.count (C);
161
+ });
162
+ Chunks.erase (UnwantedChunks, Chunks.end ());
163
+
164
+ } while (!UninterestingChunks.empty () || increaseGranularity (Chunks));
165
+
166
+ // If we reduced the testcase replace it
167
+ if (ReducedProgram)
168
+ Test.setProgram (std::move (ReducedProgram));
169
+ outs () << " Couldn't increase anymore.\n " ;
170
+ outs () << " ----------------------------\n " ;
171
+ }
0 commit comments