source/PostprocessTraces.cpp (116 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include <SpartaWorkQueue.h> #include <mariana-trench/Dependencies.h> #include <mariana-trench/Methods.h> #include <mariana-trench/PostprocessTraces.h> #include <mariana-trench/Registry.h> namespace marianatrench { namespace { bool is_valid_generation( const Method* MT_NULLABLE callee, const AccessPath& callee_port, const Kind* kind, const Registry& registry) { if (callee == nullptr) { // Leaf frame return true; } else if (callee_port.root().is_anchor()) { // Crtex frames which have canonical names instantiated during // `Frame::propagate` will have callee_ports `Anchor`. These are considered // leaves when it comes to traces (no next hop), even though the `Frame` // itself is not a leaf (has a callee). return true; } auto model = registry.get(callee); auto sources = model.generations().raw_read(callee_port).root(); return sources.contains_kind(kind); } bool is_valid_sink( const Method* MT_NULLABLE callee, const AccessPath& callee_port, const Kind* kind, const Registry& registry) { if (callee == nullptr) { // Leaf frame return true; } else if (callee_port.root().is_anchor()) { // Crtex frames which have canonical names instantiated during // `Frame::propagate` will have callee_ports `Anchor`. These are considered // leaves when it comes to traces (no next hop), even though the `Frame` // itself is not a leaf (has a callee). return true; } auto model = registry.get(callee); auto sinks = model.sinks().raw_read(callee_port).root(); if (sinks.contains_kind(kind)) { return true; } // For triggered kinds, this is trickier. Its callee's kind (frame_kind) // could be a PartialKind that turned into a TriggeredPartialKind in the // caller (sink_kind). The sink is valid as long as its underlying partial // kind matches that of its callee's. const auto* sink_triggered_kind = kind->as<TriggeredPartialKind>(); if (!sink_triggered_kind) { return false; } return sinks.contains_kind(sink_triggered_kind->partial_kind()); } TaintAccessPathTree cull_collapsed_generations( TaintAccessPathTree generation_tree, const Registry& registry) { generation_tree.map([&](Taint& generation_taint) { generation_taint.filter_invalid_frames([&](const Method* MT_NULLABLE callee, const AccessPath& callee_port, const Kind* kind) { return is_valid_generation(callee, callee_port, kind, registry); }); }); return generation_tree; } TaintAccessPathTree cull_collapsed_sinks( TaintAccessPathTree sink_tree, const Registry& registry) { sink_tree.map([&](Taint& sink_taint) { sink_taint.filter_invalid_frames([&](const Method* MT_NULLABLE callee, const AccessPath& callee_port, const Kind* kind) { return is_valid_sink(callee, callee_port, kind, registry); }); }); return sink_tree; } IssueSet cull_collapsed_issues(IssueSet issues, const Registry& registry) { issues.map([&](Issue& issue) { issue.filter_sources([&](const Method* MT_NULLABLE callee, const AccessPath& callee_port, const Kind* kind) { return is_valid_generation(callee, callee_port, kind, registry); }); issue.filter_sinks([&](const Method* MT_NULLABLE callee, const AccessPath& callee_port, const Kind* kind) { return is_valid_sink(callee, callee_port, kind, registry); }); }); return issues; } } // namespace void PostprocessTraces::remove_collapsed_traces( Registry& registry, const Context& context) { // We need to compute a decreasing fixpoint since we might remove empty // generations or sinks that are referenced in other models. auto methods = std::make_unique<ConcurrentSet<const Method*>>(); for (const auto* method : *context.methods) { methods->insert(method); } while (methods->size() > 0) { auto new_methods = std::make_unique<ConcurrentSet<const Method*>>(); auto queue = sparta::work_queue<const Method*>( [&](const Method* method) { const auto old_model = registry.get(method); auto model = old_model; model.set_generations( cull_collapsed_generations(model.generations(), registry)); model.set_sinks(cull_collapsed_sinks(model.sinks(), registry)); model.set_issues(cull_collapsed_issues(model.issues(), registry)); if (!old_model.leq(model)) { for (const auto* dependency : context.dependencies->dependencies(method)) { new_methods->insert(dependency); } } registry.set(model); }, sparta::parallel::default_num_threads()); for (const auto* method : *methods) { queue.add_item(method); } queue.run_all(); methods = std::move(new_methods); } } } // namespace marianatrench