1 /** 2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved. 3 License: MPL-2 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 This Source Code Form is subject to the terms of the Mozilla Public License, 7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 8 one at http://mozilla.org/MPL/2.0/. 9 */ 10 module dextool.plugin.fuzzer.frontend.frontend; 11 12 import std.regex : Regex; 13 import std.typecons : Nullable; 14 15 import logger = std.experimental.logger; 16 17 import dextool.compilation_db; 18 import dextool.type; 19 20 import dextool.plugin.types; 21 22 import dextool.plugin.fuzzer.type; 23 24 import dextool.plugin.fuzzer.frontend.raw_args : RawConfiguration, XmlConfig, Symbols; 25 import dextool.plugin.fuzzer.backend.interface_ : Controller, Parameter, Product, Transform; 26 27 private struct FileData { 28 import dextool.type : FileName, WriteStrategy; 29 30 invariant { 31 // cant have data in both. 32 assert(str_data.length == 0 || raw_data.length == 0); 33 } 34 35 FileName filename; 36 string str_data; 37 const(void)[] raw_data; 38 WriteStrategy strategy; 39 40 const(void)[] data() { 41 if (str_data.length != 0) { 42 return cast(void[]) str_data; 43 } else { 44 return raw_data; 45 } 46 } 47 } 48 49 class FuzzerFrontend : Controller, Parameter, Product, Transform { 50 import std.regex : regex, Regex; 51 import std.typecons : Flag; 52 import dextool.compilation_db : CompileCommandFilter; 53 import dextool.type : FileName; 54 import cpptooling.testdouble.header_filter : TestDoubleIncludes, LocationType; 55 import dsrcgen.cpp : CppModule, CppHModule; 56 57 private { 58 static const hdrExt = ".hpp"; 59 static const implExt = ".cpp"; 60 static const xmlExt = ".xml"; 61 static const rawExt = ".bin"; 62 63 CustomHeader custom_hdr; 64 65 /// Output directory to generate data in such as code. 66 DirName output_dir; 67 68 /// Used to match symbols by their location. 69 Regex!char[] exclude; 70 Regex!char[] restrict; 71 72 /// Data produced by the generatore intented to be written to specified file. 73 FileData[] file_data; 74 75 CompileCommandFilter compiler_flag_filter; 76 Symbols symbols; 77 } 78 79 static auto make(ref RawConfiguration args) { 80 // dfmt off 81 auto r = new FuzzerFrontend(DirName(args.out_)) 82 .argFileExclude(args.fileExclude) 83 .argFileRestrict(args.fileRestrict) 84 .argXmlConfig(args.xmlConfig); 85 // dfmt on 86 return r; 87 } 88 89 this(DirName output_dir) { 90 this.output_dir = output_dir; 91 } 92 93 auto argFileExclude(string[] a) { 94 import std.array : array; 95 import std.algorithm : map; 96 97 this.exclude = a.map!(a => regex(a)).array(); 98 return this; 99 } 100 101 auto argFileRestrict(string[] a) { 102 import std.array : array; 103 import std.algorithm : map; 104 105 this.restrict = a.map!(a => regex(a)).array(); 106 return this; 107 } 108 109 /// Ensure that the relevant information from the xml file is extracted. 110 auto argXmlConfig(Nullable!XmlConfig conf) { 111 import dextool.compilation_db : defaultCompilerFlagFilter; 112 113 if (conf.isNull) { 114 compiler_flag_filter = CompileCommandFilter(defaultCompilerFlagFilter, 1); 115 return this; 116 } 117 118 compiler_flag_filter = CompileCommandFilter(conf.get.filterClangFlags, 119 conf.get.skipCompilerArgs); 120 symbols = conf.get.symbols; 121 122 return this; 123 } 124 125 ref CompileCommandFilter getCompileCommandFilter() { 126 return compiler_flag_filter; 127 } 128 129 /// Data produced by the generatore intented to be written to specified file. 130 ref FileData[] getProducedFiles() { 131 return file_data; 132 } 133 134 ref Symbols getSymbols() { 135 return symbols; 136 } 137 138 // -- Controller -- 139 140 @safe bool doSymbolAtLocation(const string filename, const string symbol) { 141 import dextool.plugin.regex_matchers : matchAny; 142 143 // if there are no filter registered then it automatically passes. 144 145 bool restrict_pass = restrict.length == 0 || matchAny(filename, restrict); 146 debug logger.tracef(!restrict_pass, 147 "--file-restrict skipping: %s in %s", symbol, filename); 148 149 bool exclude_pass = exclude.length == 0 || !matchAny(filename, exclude); 150 debug logger.tracef(!exclude_pass, "--file-exclude skipping: %s in %s", symbol, filename); 151 152 return restrict_pass && exclude_pass; 153 } 154 155 bool doSymbol(string symbol) { 156 if (auto sym = symbols.lookup(FullyQualifiedNameType(symbol))) { 157 if (sym.filter == Symbol.FilterKind.exclude) { 158 return false; 159 } 160 } 161 162 return true; 163 } 164 165 // -- Parameters -- 166 167 DextoolVersion getToolVersion() { 168 import dextool.utility : dextoolVersion; 169 170 return dextoolVersion; 171 } 172 173 CustomHeader getCustomHeader() { 174 return custom_hdr; 175 } 176 177 // -- Products -- 178 179 void putFile(FileName fname, CppHModule hdr_data) { 180 file_data ~= FileData(fname, hdr_data.render()); 181 } 182 183 void putFile(FileName fname, CppModule impl_data, 184 WriteStrategy strategy = WriteStrategy.overwrite) { 185 file_data ~= FileData(fname, impl_data.render(), null, strategy); 186 } 187 188 void putFile(FileName fname, const(ubyte)[] raw_data) { 189 file_data ~= FileData(fname, null, raw_data); 190 } 191 192 void putFile(FileName fname, string raw_data, WriteStrategy strategy = WriteStrategy.overwrite) { 193 file_data ~= FileData(fname, null, raw_data, strategy); 194 } 195 196 // -- Transform -- 197 FileName createHeaderFile(string name) { 198 import std.path : buildPath; 199 200 return FileName(buildPath(output_dir, name ~ hdrExt)); 201 } 202 203 FileName createImplFile(string name) { 204 import std.path : buildPath; 205 206 return FileName(buildPath(output_dir, name ~ implExt)); 207 } 208 209 FileName createFuzzCase(string name, ulong id) { 210 import std.conv : to; 211 import std.path : buildPath; 212 213 return FileName(buildPath(output_dir, name ~ id.to!string ~ implExt)); 214 } 215 216 FileName createFuzzyDataFile(string name) { 217 import std.path : buildPath; 218 219 return FileName(buildPath(output_dir, "test_case", name ~ rawExt)); 220 } 221 222 // try the darnest to not overwrite an existing config. 223 FileName createXmlConfigFile(string name) { 224 import std.conv : to; 225 import std.path : buildPath; 226 import std.file : exists; 227 228 string p = buildPath(output_dir, name ~ xmlExt); 229 230 for (int i = 0; exists(p); ++i) { 231 p = buildPath(output_dir, name ~ i.to!string() ~ xmlExt); 232 } 233 234 return FileName(p); 235 } 236 } 237 238 auto genFuzzer(FuzzerFrontend frontend, in string[] in_cflags, 239 CompileCommandDB compile_db, InFiles in_files, Regex!char strip_incl) { 240 import dextool.io : writeFileData; 241 import dextool.plugin.fuzzer.backend.backend : Backend; 242 import dextool.utility : prependDefaultFlags, PreferLang; 243 244 const auto user_cflags = prependDefaultFlags(in_cflags, PreferLang.none); 245 const auto total_files = in_files.length; 246 auto backend = Backend(frontend, frontend, frontend, frontend, strip_incl); 247 248 foreach (idx, in_file; in_files) { 249 logger.infof("File %d/%d ", idx + 1, total_files); 250 string[] use_cflags; 251 AbsolutePath analyze_file; 252 253 if (compile_db.length > 0) { 254 auto db_search_result = compile_db.appendOrError(user_cflags, 255 in_file, frontend.getCompileCommandFilter); 256 if (db_search_result.isNull) { 257 return ExitStatusType.Errors; 258 } 259 use_cflags = db_search_result.get.cflags; 260 analyze_file = db_search_result.get.absoluteFile; 261 } else { 262 use_cflags = user_cflags.dup; 263 analyze_file = AbsolutePath(FileName(in_file)); 264 } 265 266 if (backend.analyzeFile(analyze_file, use_cflags) == ExitStatusType.Errors) { 267 return ExitStatusType.Errors; 268 } 269 } 270 271 backend.finalizeIncludes; 272 273 // Analyse and generate interceptors 274 backend.process(frontend.getSymbols, frontend.getCompileCommandFilter); 275 276 return writeFileData(frontend.getProducedFiles); 277 }