diff --git a/Cargo.toml b/Cargo.toml
index 5c6f4517d75407c094c58cc20e6e95a4c5a0edfc..11c23a9cf42af94ede368a51574dd6e2643f143f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,3 +6,8 @@ edition = "2018"
 
 [dependencies]
 libc = "0.2.43"
+pest = "2.0.2"
+pest_derive = "2.0.1"
+stack-sizes = "0.1.0"
+rustc-demangle = "0.1.9"
+petgraph = "0.4.13"
diff --git a/src/main.rs b/src/main.rs
index 2eb47d8e85bad5970eca2e3304313946c6add051..cca278b0c94d0f1cc764c8c9c5be7685104e9ea7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,6 +4,30 @@ use std::io::{self, Read, Write};
 use std::process::{Command, Stdio};
 use std::{env, str};
 
+#[macro_use]
+extern crate pest_derive;
+extern crate rustc_demangle;
+extern crate stack_sizes;
+
+use pest::Parser;
+use rustc_demangle::demangle;
+
+use std::fs::File;
+
+use petgraph::{
+    algo::is_cyclic_directed,
+    dot::{Config, Dot},
+    graph::NodeIndex,
+    visit::DfsPostOrder,
+    Graph, Incoming,
+};
+use std::collections::hash_map::HashMap;
+use std::fmt;
+
+#[derive(Parser)]
+#[grammar = "ident.pest"]
+struct IdentParser;
+
 #[derive(Debug)]
 struct Out {
     hash: Option<String>,
@@ -47,6 +71,112 @@ fn parse_out(out_str: &str) -> Out {
     out
 }
 
+struct Node {
+    name: String,
+    local_stack: u32,
+    call_stack: u32,
+}
+
+impl fmt::Debug for Node {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(
+            f,
+            "{:#}\\n stack local = {}, call = {}",
+            demangle(&self.name),
+            self.local_stack,
+            self.call_stack
+        )
+    }
+}
+
+fn new_node(
+    hm: &mut HashMap<String, NodeIndex<u32>>,
+    g: &mut Graph<Node, ()>,
+    s: String,
+    v: u32,
+) -> NodeIndex<u32> {
+    let n = Node {
+        name: s.to_string(),
+        local_stack: v,
+        call_stack: v,
+    };
+    let i = g.add_node(n);
+    hm.insert(s, i);
+    i
+}
+
+fn add_edge<'a>(
+    hm: &mut HashMap<String, NodeIndex<u32>>,
+    g: &mut Graph<Node, ()>,
+    from: &str,
+    to: &str,
+) {
+    let i_from = hm.get(from).expect("from not found");
+    if let Some(i_to) = hm.get(to) {
+        g.add_edge(*i_from, *i_to, ());
+    }
+}
+
+fn read_elf(path: &str, buffer: &mut Vec<u8>) {
+    let mut elf = File::open(path).expect(format!("error opening '{}'", path).as_str());
+    elf.read_to_end(buffer)
+        .expect(format!("error opening '{}'", path).as_str());
+}
+
+fn parse_elf<'a>(
+    hm: &mut HashMap<String, NodeIndex<u32>>,
+    g: &mut Graph<Node, ()>,
+    path: &str,
+    vec: &'a mut Vec<u8>,
+) {
+    read_elf(path, vec);
+
+    // read and analyse stack-sizes of elf
+    let ss = stack_sizes::analyze(&vec).expect("error analysing elf");
+    if let Some(v32) = ss.left() {
+        // put into graph
+        for s in v32.iter() {
+            let d_name = s.name().to_string(); //  format!("{:#}", demangle(s.name()));
+            let _ = new_node(hm, g, d_name, s.stack() as u32);
+        }
+    } else {
+        panic!("64 bit elfs not supported");
+    }
+}
+
+fn parse_callgraph(hm: &mut HashMap<String, NodeIndex<u32>>, g: &mut Graph<Node, ()>, input: &str) {
+    let functions = IdentParser::parse(Rule::ident_list, &input)
+        .unwrap_or_else(|e| panic!("parse error {}", e));
+
+    for f in functions {
+        match f.as_rule() {
+            Rule::fun => {
+                let mut p = f.into_inner();
+                let mut i = p.next().unwrap().into_inner();
+                let from = i.next().unwrap().into_inner();
+
+                for c in p {
+                    match c.as_rule() {
+                        Rule::callext => {}
+                        Rule::callfun => {
+                            let mut p = c.into_inner();
+                            p.next().unwrap();
+                            let to = p.next().unwrap().into_inner().as_str().to_string();
+                            add_edge(hm, g, from.as_str(), to.as_str());
+                        }
+                        _ => panic!("internal error"),
+                    }
+                }
+            }
+            Rule::null => (),
+            Rule::EOI => (),
+            _ => {
+                panic!("internal error");
+            }
+        }
+    }
+}
+
 fn main() {
     println!("start sub command");
     // first argument is the path to this binary; the second argument is always "call-stack" -- both can
@@ -107,26 +237,61 @@ fn main() {
     let mut s = String::new();
     write!(
         &mut s,
-        "{}/{}{}.ll",
+        "{}/{}{}",
         out.out_dir.expect("--out-dir missing"),
         out.crate_name.expect("--crate-name missing"),
         out.hash.expect("extra-filename missing")
     )
     .expect("internal error");
 
-    println!("s: {}", s);
+    let mut ir = s.clone();
+    write!(&mut ir, ".ll").expect("internal error");
+    println!("s: {}", ir);
 
     let mut c = Command::new("opt");
     c.arg("-analyze")
         .arg("-print-callgraph")
-        .arg(s)
+        .arg(ir)
         .output()
         .expect("failed to execute process");
     let dump = c.output().expect("failed to execute process");
-    println!("{:?}", dump);
-    println!("{:?}", dump.status);
-    println!("{:?}", str::from_utf8(&dump.stdout).unwrap());
-    println!("{:?}", str::from_utf8(&dump.stderr).unwrap());
+    //println!("{:?}", dump);
+    println!("\nstatus: {:?}", dump.status);
+    println!("\nstdout: {:?}", str::from_utf8(&dump.stdout).unwrap());
+    println!("\nstderr: {:?}", str::from_utf8(&dump.stderr).unwrap());
+
+    let mut g = Graph::<_, ()>::new();
+    let mut hm = HashMap::new();
+
+    let mut buffer = Vec::new();
+    println!("elf = {}", &s);
+
+    parse_elf(&mut hm, &mut g, &s, &mut buffer);
+
+    parse_callgraph(&mut hm, &mut g, &str::from_utf8(&dump.stderr).unwrap());
+
+    if is_cyclic_directed(&g) {
+        println!("/* warning, cyclic graph */");
+    }
+
+    let mut v = Vec::new();
+    for ext in g.externals(Incoming) {
+        v.push(ext);
+    }
+
+    let mut dfs = DfsPostOrder::empty(&g);
+    while let Some(ext) = v.pop() {
+        dfs.move_to(ext);
+        while let Some(node) = dfs.next(&g) {
+            // use a detached neighbors walker
+            let mut edges = g.neighbors_directed(node, Incoming).detach();
+            while let Some(to) = edges.next_node(&g) {
+                g[to].call_stack = g[to].call_stack.max(g[to].local_stack + g[node].call_stack);
+            }
+        }
+    }
+
+    println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel]));
 
     // let hello = c.stdout();