diff --git a/hercules_ir/src/dot.rs b/hercules_ir/src/dot.rs
index 6d5e8a77a156c43a23ea87d882c4a8f2d7920171..9c6c5f174a4c3b1c4a19ce15597e5385ddae7aa4 100644
--- a/hercules_ir/src/dot.rs
+++ b/hercules_ir/src/dot.rs
@@ -15,6 +15,7 @@ use crate::*;
  */
 pub fn xdot_module(
     module: &ir::Module,
+    func_selection: &Vec<bool>,
     reverse_postorders: &Vec<Vec<NodeID>>,
     typing: Option<&ModuleTyping>,
     doms: Option<&Vec<DomTree>>,
@@ -30,6 +31,7 @@ pub fn xdot_module(
     let mut contents = String::new();
     write_dot(
         &module,
+        func_selection,
         &reverse_postorders,
         typing,
         doms,
@@ -54,6 +56,7 @@ pub fn xdot_module(
  */
 pub fn write_dot<W: Write>(
     module: &ir::Module,
+    func_selection: &Vec<bool>,
     reverse_postorders: &Vec<Vec<NodeID>>,
     typing: Option<&ModuleTyping>,
     doms: Option<&Vec<DomTree>>,
@@ -64,7 +67,10 @@ pub fn write_dot<W: Write>(
 ) -> std::fmt::Result {
     write_digraph_header(w)?;
 
-    for function_id in (0..module.functions.len()).map(FunctionID::new) {
+    for function_id in (0..module.functions.len())
+        .filter(|idx| func_selection[*idx])
+        .map(FunctionID::new)
+    {
         let function = &module.functions[function_id.idx()];
         let reverse_postorder = &reverse_postorders[function_id.idx()];
         let mut reverse_postorder_node_numbers = vec![0; function.nodes.len()];
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index e9c681cd1239fca6cc1946c55032837eff7c2f00..9c7391acd5686d95461ed562f61b3dbc9774651a 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -2328,6 +2328,22 @@ fn run_pass(
                 None => true,
             };
 
+            let mut bool_selection = vec![];
+            if let Some(selection) = selection {
+                bool_selection = vec![false; pm.functions.len()];
+                for loc in selection {
+                    let CodeLocation::Function(id) = loc else {
+                        return Err(SchedulerError::PassError {
+                        pass: "xdot".to_string(),
+                        error: "expected coarse-grained selection (can't partially xdot a function)".to_string(),
+                    });
+                    };
+                    bool_selection[id.idx()] = true;
+                }
+            } else {
+                bool_selection = vec![true; pm.functions.len()];
+            }
+
             pm.make_reverse_postorders();
             if force_analyses {
                 pm.make_typing();
@@ -2345,6 +2361,7 @@ fn run_pass(
             pm.with_mod(|module| {
                 xdot_module(
                     module,
+                    &bool_selection,
                     &reverse_postorders,
                     typing.as_ref(),
                     doms.as_ref(),