|
| 1 | +package pascal.taie.analysis.bugfinder.pathsensnullpointer; |
| 2 | + |
| 3 | +import com.fasterxml.jackson.databind.ObjectMapper; |
| 4 | +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; |
| 5 | +import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; |
| 6 | +import org.apache.logging.log4j.LogManager; |
| 7 | +import org.apache.logging.log4j.Logger; |
| 8 | +import pascal.taie.World; |
| 9 | +import pascal.taie.analysis.ProgramAnalysis; |
| 10 | +import pascal.taie.analysis.bugfinder.pathsensnullpointer.npanalysis.PathSensNullPointerAnalysis; |
| 11 | +import pascal.taie.analysis.graph.icfg.ICFG; |
| 12 | +import pascal.taie.analysis.graph.icfg.ICFGBuilder; |
| 13 | +import pascal.taie.config.AnalysisConfig; |
| 14 | +import pascal.taie.ir.stmt.Stmt; |
| 15 | +import pascal.taie.language.classes.JMethod; |
| 16 | +import pascal.taie.util.collection.Maps; |
| 17 | + |
| 18 | +import java.io.File; |
| 19 | +import java.io.FileWriter; |
| 20 | +import java.io.IOException; |
| 21 | +import java.io.PrintStream; |
| 22 | +import java.util.ArrayList; |
| 23 | +import java.util.List; |
| 24 | +import java.util.Map; |
| 25 | +import java.util.Set; |
| 26 | + |
| 27 | +/** |
| 28 | + * Process null pointer detection results and generate readable reports |
| 29 | + */ |
| 30 | +public class NullPointerResultProcessor extends ProgramAnalysis<Void> { |
| 31 | + |
| 32 | + public static final String ID = "nullpointer-result-processor"; |
| 33 | + |
| 34 | + private static final Logger logger = LogManager.getLogger(NullPointerResultProcessor.class); |
| 35 | + |
| 36 | + private static final String RESULTS_FILE_TEMPLATE = "nullpointer-results-%s.txt"; |
| 37 | + |
| 38 | + private static final String RESULTS_YAML_FILE_TEMPLATE = "nullpointer-results-%s.yml"; |
| 39 | + |
| 40 | + private static final boolean ENABLE_TEXT_OUTPUT = false; |
| 41 | + |
| 42 | + private static final boolean PRINT_PROPAGATION_PATH = false; |
| 43 | + |
| 44 | + public NullPointerResultProcessor(AnalysisConfig config) { |
| 45 | + super(config); |
| 46 | + } |
| 47 | + |
| 48 | + @Override |
| 49 | + public Void analyze() { |
| 50 | + List<PathSensNullPointerAnalysis.NullPointerPath> results = |
| 51 | + World.get().getResult(PathSensNullPointerAnalysis.ID); |
| 52 | + List<List<Stmt>> uninitializedResult = World.get().getResult(NullPointerAnalysis.UNINITIALIZED_RESULT); |
| 53 | + Set<Stmt> containerResults = World.get().getResult(NullPointerAnalysis.CONTAINER_NULL_RESULT); |
| 54 | + String mainClassName = World.get().getMainMethod().getDeclaringClass().getName(); |
| 55 | + if (ENABLE_TEXT_OUTPUT) { |
| 56 | + dumpResultsToText(results, uninitializedResult, containerResults, mainClassName); |
| 57 | + } |
| 58 | + |
| 59 | + dumpResultsToYaml(results, uninitializedResult, containerResults, mainClassName); |
| 60 | + |
| 61 | + return null; |
| 62 | + } |
| 63 | + |
| 64 | + /** |
| 65 | + * Extract location information from a statement |
| 66 | + * @return Map containing class name, method name and line number |
| 67 | + */ |
| 68 | + private static Map<String, String> extractLocationInfo(Stmt stmt) { |
| 69 | + Map<String, String> location = Maps.newLinkedHashMap(); |
| 70 | + ICFG<JMethod, Stmt> icfg = World.get().getResult(ICFGBuilder.ID); |
| 71 | + JMethod method = icfg.getContainingMethodOf(stmt); |
| 72 | + location.put("class", method.getDeclaringClass().getName()); |
| 73 | + location.put("method", method.getSignature()); |
| 74 | + location.put("line", String.valueOf(stmt.getLineNumber())); |
| 75 | + return location; |
| 76 | + } |
| 77 | + |
| 78 | + private void dumpResultsToText(List<PathSensNullPointerAnalysis.NullPointerPath> results, |
| 79 | + List<List<Stmt>> uninitializedFields, |
| 80 | + Set<Stmt> containerResults, |
| 81 | + String mainClassName) { |
| 82 | + String fileName = String.format(RESULTS_FILE_TEMPLATE, mainClassName); |
| 83 | + File outFile = new File(World.get().getOptions().getOutputDir(), fileName); |
| 84 | + try (PrintStream out = new PrintStream(outFile)) { |
| 85 | + logger.info("Dumping null pointer detection results to {}", outFile.getAbsolutePath()); |
| 86 | + |
| 87 | + out.println("Found " + results.size() + " potential null pointer issues:"); |
| 88 | + out.println(); |
| 89 | + |
| 90 | + for (int i = 0; i < results.size(); i++) { |
| 91 | + PathSensNullPointerAnalysis.NullPointerPath path = results.get(i); |
| 92 | + out.printf("Issue #%d (Null Pointer Dereference):%n", i + 1); |
| 93 | + |
| 94 | + // Print dereference location information |
| 95 | + Map<String, String> derefLocation = extractLocationInfo(path.dereferenceSite()); |
| 96 | + out.printf(" Null Pointer Dereference: Class: %s, Method: %s, Line: %s%n", |
| 97 | + derefLocation.get("class"), derefLocation.get("method"), derefLocation.get("line")); |
| 98 | + |
| 99 | + // Print null value source location information |
| 100 | + Map<String, String> nullLocation = extractLocationInfo(path.nullSourceSite()); |
| 101 | + out.printf(" Null Value Source: Class: %s, Method: %s, Line: %s%n", |
| 102 | + nullLocation.get("class"), nullLocation.get("method"), nullLocation.get("line")); |
| 103 | + |
| 104 | + if (PRINT_PROPAGATION_PATH) { |
| 105 | + out.println(" Propagation Path:"); |
| 106 | + for (Stmt stmt : path.path()) { |
| 107 | + Map<String, String> pathLocation = extractLocationInfo(stmt); |
| 108 | + out.printf(" [%s:%s:%s] %s%n", |
| 109 | + pathLocation.get("class"), |
| 110 | + pathLocation.get("method"), |
| 111 | + pathLocation.get("line"), |
| 112 | + stmt); |
| 113 | + } |
| 114 | + } |
| 115 | + out.println(); |
| 116 | + } |
| 117 | + |
| 118 | + if (uninitializedFields != null && !uninitializedFields.isEmpty()) { |
| 119 | + out.println("Found " + uninitializedFields.size() + " potential uninitialized field issues:"); |
| 120 | + out.println(); |
| 121 | + for (int i = 0; i < uninitializedFields.size(); i++) { |
| 122 | + Stmt stmt = uninitializedFields.get(i).get(0); |
| 123 | + Map<String, String> location = extractLocationInfo(stmt); |
| 124 | + out.printf("Issue #%d (Uninitialized Field): Class: %s, Method: %s, Line: %s%n", |
| 125 | + i + 1, location.get("class"), location.get("method"), location.get("line")); |
| 126 | + out.printf(" Statement: %s%n", stmt); |
| 127 | + out.println(); |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + if (containerResults != null && !containerResults.isEmpty()) { |
| 132 | + out.println("Found " + containerResults.size() + " potential null container access issues:"); |
| 133 | + out.println(); |
| 134 | + int i = 0; |
| 135 | + for (Stmt stmt : containerResults) { |
| 136 | + Map<String, String> location = extractLocationInfo(stmt); |
| 137 | + out.printf("Issue #%d (Null Container Access): Class: %s, Method: %s, Line: %s%n", |
| 138 | + i + 1, location.get("class"), location.get("method"), location.get("line")); |
| 139 | + out.printf(" Statement: %s%n", stmt); |
| 140 | + out.println(); |
| 141 | + i++; |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + } catch (IOException e) { |
| 146 | + logger.error("Failed to open output file {}", outFile); |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + private void dumpResultsToYaml(List<PathSensNullPointerAnalysis.NullPointerPath> results, |
| 151 | + List<List<Stmt>> uninitializedFields, |
| 152 | + Set<Stmt> containerResults, |
| 153 | + String mainClassName) { |
| 154 | + String fileName = String.format(RESULTS_YAML_FILE_TEMPLATE, mainClassName); |
| 155 | + File outFile = new File(World.get().getOptions().getOutputDir(), fileName); |
| 156 | + logger.info("Dumping null pointer detection results in YAML format to {}", outFile.getAbsolutePath()); |
| 157 | + |
| 158 | + Map<String, Object> dumpData = Maps.newLinkedHashMap(); |
| 159 | + |
| 160 | + // Process Null Pointer Dereference issues |
| 161 | + List<Map<String, Object>> nullPointerIssues = new ArrayList<>(); |
| 162 | + for (int i = 0; i < results.size(); i++) { |
| 163 | + PathSensNullPointerAnalysis.NullPointerPath path = results.get(i); |
| 164 | + Map<String, Object> result = Maps.newLinkedHashMap(); |
| 165 | + |
| 166 | + result.put("issue-type", "Null Pointer Dereference"); |
| 167 | + result.put("issue-number", i + 1); |
| 168 | + |
| 169 | + // Add dereference location information |
| 170 | + Map<String, String> derefLocation = extractLocationInfo(path.dereferenceSite()); |
| 171 | + Map<String, Object> derefInfo = Maps.newLinkedHashMap(); |
| 172 | + derefInfo.put("class", derefLocation.get("class")); |
| 173 | + derefInfo.put("method", derefLocation.get("method")); |
| 174 | + derefInfo.put("line", derefLocation.get("line")); |
| 175 | + derefInfo.put("statement", path.dereferenceSite().toString()); |
| 176 | + result.put("dereference-site", derefInfo); |
| 177 | + |
| 178 | + // Add null value source location information |
| 179 | + Map<String, String> nullLocation = extractLocationInfo(path.nullSourceSite()); |
| 180 | + Map<String, Object> nullInfo = Maps.newLinkedHashMap(); |
| 181 | + nullInfo.put("class", nullLocation.get("class")); |
| 182 | + nullInfo.put("method", nullLocation.get("method")); |
| 183 | + nullInfo.put("line", nullLocation.get("line")); |
| 184 | + nullInfo.put("statement", path.nullSourceSite().toString()); |
| 185 | + result.put("null-source-site", nullInfo); |
| 186 | + |
| 187 | + if (PRINT_PROPAGATION_PATH) { |
| 188 | + List<Map<String, Object>> pathInfo = new ArrayList<>(); |
| 189 | + for (Stmt stmt : path.path()) { |
| 190 | + Map<String, String> pathLocation = extractLocationInfo(stmt); |
| 191 | + Map<String, Object> stmtInfo = Maps.newLinkedHashMap(); |
| 192 | + stmtInfo.put("class", pathLocation.get("class")); |
| 193 | + stmtInfo.put("method", pathLocation.get("method")); |
| 194 | + stmtInfo.put("line", pathLocation.get("line")); |
| 195 | + stmtInfo.put("statement", stmt.toString()); |
| 196 | + pathInfo.add(stmtInfo); |
| 197 | + } |
| 198 | + result.put("propagation-path", pathInfo); |
| 199 | + } |
| 200 | + nullPointerIssues.add(result); |
| 201 | + } |
| 202 | + dumpData.put("total-null-pointer-dereference-issues", results.size()); |
| 203 | + dumpData.put("null-pointer-dereference-issues", nullPointerIssues); |
| 204 | + |
| 205 | + // Process Uninitialized Field issues |
| 206 | + if (uninitializedFields != null && !uninitializedFields.isEmpty()) { |
| 207 | + List<Map<String, Object>> uninitializedFieldIssues = new ArrayList<>(); |
| 208 | + for (int i = 0; i < uninitializedFields.size(); i++) { |
| 209 | + Stmt stmt = uninitializedFields.get(i).get(0); |
| 210 | + Map<String, Object> issue = Maps.newLinkedHashMap(); |
| 211 | + issue.put("issue-type", "Uninitialized Field"); |
| 212 | + putStmt(uninitializedFieldIssues, i, stmt, issue); |
| 213 | + } |
| 214 | + dumpData.put("total-uninitialized-field-issues", uninitializedFields.size()); |
| 215 | + dumpData.put("uninitialized-field-issues", uninitializedFieldIssues); |
| 216 | + } |
| 217 | + |
| 218 | + // Process Null Container Access issues |
| 219 | + if (containerResults != null && !containerResults.isEmpty()) { |
| 220 | + List<Map<String, Object>> nullContainerAccessIssues = new ArrayList<>(); |
| 221 | + int i = 0; |
| 222 | + for (Stmt stmt : containerResults) { |
| 223 | + Map<String, Object> issue = Maps.newLinkedHashMap(); |
| 224 | + issue.put("issue-type", "Null Container Access"); |
| 225 | + putStmt(nullContainerAccessIssues, i, stmt, issue); |
| 226 | + i++; |
| 227 | + } |
| 228 | + dumpData.put("total-null-container-access-issues", containerResults.size()); |
| 229 | + dumpData.put("null-container-access-issues", nullContainerAccessIssues); |
| 230 | + } |
| 231 | + |
| 232 | + try (FileWriter writer = new FileWriter(outFile)) { |
| 233 | + ObjectMapper mapper = new ObjectMapper(new YAMLFactory() |
| 234 | + .enable(YAMLGenerator.Feature.INDENT_ARRAYS) |
| 235 | + .enable(YAMLGenerator.Feature.ALLOW_LONG_KEYS) |
| 236 | + .enable(YAMLGenerator.Feature.INDENT_ARRAYS_WITH_INDICATOR) |
| 237 | + .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER) |
| 238 | + .disable(YAMLGenerator.Feature.SPLIT_LINES) |
| 239 | + ); |
| 240 | + mapper.writeValue(writer, dumpData); |
| 241 | + } catch (IOException e) { |
| 242 | + logger.error("Failed to open output file {}", outFile); |
| 243 | + } |
| 244 | + } |
| 245 | + |
| 246 | + private void putStmt(List<Map<String, Object>> nullContainerAccessIssues, int i, Stmt stmt, Map<String, Object> issue) { |
| 247 | + issue.put("issue-number", i + 1); |
| 248 | + Map<String, String> location = extractLocationInfo(stmt); |
| 249 | + issue.put("class", location.get("class")); |
| 250 | + issue.put("method", location.get("method")); |
| 251 | + issue.put("line", location.get("line")); |
| 252 | + issue.put("statement", stmt.toString()); |
| 253 | + nullContainerAccessIssues.add(issue); |
| 254 | + } |
| 255 | +} |
0 commit comments