Skip to content
This repository was archived by the owner on Mar 6, 2024. It is now read-only.

Commit

Permalink
WASM/EOS functions instructions analytics
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrick Ventuzelo committed Aug 8, 2018
1 parent 1c17b59 commit 568280e
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 34 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,30 @@ graph.view_functions()
<img src="/images/wasm-cfg-fib.png" height="400px"/>
</p>


#### Functions' instructions analytics

```python
from octopus.arch.wasm.cfg import WasmCFG

# complete wasm module
file_name = "examples/wasm/samples/hello_wasm_studio.wasm"

# read file
with open(file_name, 'rb') as f:
raw = f.read()

# create the cfg
cfg = WasmCFG(raw)

# visualization
cfg.visualize_instrs_per_funcs()
```

<p align="center">
<img src="/images/wasm-instr-func-analytics.png" height="400px"/>
</p>

#### Call Flow Analysis

```python
Expand Down
Binary file added images/wasm-instr-func-analytics.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
91 changes: 81 additions & 10 deletions octopus/arch/wasm/cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
EDGE_CONDITIONAL_TRUE, EDGE_CONDITIONAL_FALSE,
EDGE_FALLTHROUGH, EDGE_CALL)
from octopus.analysis.cfg import CFG

from octopus.analysis.graph import CFGGraph
from octopus.arch.wasm.analyzer import WasmModuleAnalyzer
from octopus.arch.wasm.disassembler import WasmDisassembler
from octopus.arch.wasm.format import (format_func_name,
format_bb_name)


from octopus.arch.wasm.wasm import _groups
from octopus.core.utils import bytecode_to_bytes
# for graph visualisation
from graphviz import Digraph
Expand Down Expand Up @@ -245,19 +245,15 @@ def enum_blocks_edges(function_id, instructions):
class WasmCFG(CFG):
"""
"""
def __init__(self, module_bytecode, static_analysis=True):

def __init__(self, module_bytecode):
self.module_bytecode = bytecode_to_bytes(module_bytecode)
self.static_analysis = static_analysis
self.analyzer = None

self.functions = list()
self.basicblocks = list()
self.edges = list()

if self.static_analysis:
self.analyzer = WasmModuleAnalyzer(self.module_bytecode)
self.run_static_analysis()
self.analyzer = WasmModuleAnalyzer(self.module_bytecode)
self.run_static_analysis()

def run_static_analysis(self):
self.functions = enum_func(self.module_bytecode)
Expand Down Expand Up @@ -311,8 +307,19 @@ def get_functions_call_edges(self, format_fname=True):

return (nodes, edges)

def visualize_call_flow(self, filename="wasm_call_graph_octopus.gv"):
def visualize(self):
"""Visualize the cfg
used CFGGraph
equivalent to:
graph = CFGGraph(cfg)
graph.view_functions()
"""
graph = CFGGraph(self)
graph.view_functions()

def visualize_call_flow(self, filename="wasm_call_graph_octopus.gv"):
"""Visualize the cfg call flow graph
"""
nodes, edges = self.get_functions_call_edges(format_fname=False)

g = Digraph(filename, filename=filename)
Expand Down Expand Up @@ -350,3 +357,67 @@ def visualize_call_flow(self, filename="wasm_call_graph_octopus.gv"):
c.edge(edge.node_from, edge.node_to, label=label)

g.render(filename, view=True)

def visualize_instrs_per_funcs(self, show=True, save=True,
out_filename="wasm_statictics.png",
fontsize=8):
"""Visualize the instructions repartitions per functions
"""

import numpy as np
import matplotlib.pyplot as plt

final = list()
datas = list()

# legend x axis - name functions
group_names = tuple([func.name for func in self.functions])
# number of functions
ind = [x for x, _ in enumerate(self.functions)]

# list all groups
all_groups = [v for _, v in _groups.items()]

# list()
for func in self.functions:
data = list()
group = [i.group for i in func.instructions]
for g in all_groups:
data.append(group.count(g))
datas.append(tuple(data))

for idx in range(len(all_groups)):
final.append(tuple([x[idx] for x in datas]))

# choice color: https://matplotlib.org/users/colormaps.html
color = iter(plt.cm.gist_rainbow(np.linspace(0, 1, len(all_groups))))
stack = np.array([0 * len(all_groups)])
for idx in range(len(all_groups)):
if idx == 0:
# first bar
plt.barh(ind, final[idx], label=all_groups[idx],
align='center', color=next(color))
else:
plt.barh(ind, final[idx], label=all_groups[idx], left=stack,
align='center', color=next(color))

stack = stack + np.array(final[idx])

# Rotate x-labels on the x-axis
plt.yticks(fontsize=fontsize)
plt.ylim([0, len(self.functions)])
plt.yticks(ind, group_names)
plt.ylabel('Functions')
plt.xlabel('Instructions count')
plt.legend(loc="lower right")
plt.title('Instructions count by function and group')

# save
if save:
plt.savefig(out_filename)
# show
if show:
plt.show()

def visualize_analytics(self):
pass
23 changes: 4 additions & 19 deletions octopus/arch/wasm/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
INSN_LEAVE_BLOCK,
INSN_NO_FLOW) # INSN_BRANCH

from octopus.arch.wasm.wasm import _groups


class WasmInstruction(Instruction):
"""Wasm Instruction
Expand Down Expand Up @@ -53,25 +55,8 @@ def __str__(self):
@property
def group(self):
""" Instruction classification per group """
classes = {0x00: 'Control',
0x1A: 'Parametric',
0x20: 'Variable',
0x28: 'Memory',
0x41: 'Constant',
0x45: 'Logical_i32',
0x50: 'Logical_i64',
0x5b: 'Logical_f32',
0x61: 'Logical_f64',
0x67: 'Arithmetic_i32',
0x71: 'Bitwise_i32',
0x79: 'Arithmetic_i64',
0x83: 'Bitwise_i64',
0x8b: 'Arithmetic_f32',
0x99: 'Arithmetic_f64',
0xa7: 'Conversion'}

last_class = classes.get(0)
for k, v in classes.items():
last_class = _groups.get(0)
for k, v in _groups.items():
if self.opcode >= k:
last_class = v
else:
Expand Down
19 changes: 18 additions & 1 deletion octopus/arch/wasm/wasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@
from wasm.immtypes import *
from wasm.opcodes import INSN_ENTER_BLOCK, INSN_LEAVE_BLOCK, INSN_BRANCH, INSN_NO_FLOW

_groups = {0x00: 'Control',
0x1A: 'Parametric',
0x20: 'Variable',
0x28: 'Memory',
0x41: 'Constant',
0x45: 'Logical_i32',
0x50: 'Logical_i64',
0x5b: 'Logical_f32',
0x61: 'Logical_f64',
0x67: 'Arithmetic_i32',
0x71: 'Bitwise_i32',
0x79: 'Arithmetic_i64',
0x83: 'Bitwise_i64',
0x8b: 'Arithmetic_f32',
0x99: 'Arithmetic_f64',
0xa7: 'Conversion'}

"""
TODO: add pop and pushes value per instructions
"""
Expand Down Expand Up @@ -170,8 +187,8 @@
0xa4: ('f64.min', None, 0, 0, 0, ''),
0xa5: ('f64.max', None, 0, 0, 0, ''),
0xa6: ('f64.copysign', None, 0, 0, 0, ''),
0xa7: ('i32.wrap/i64', None, 0, 0, 0, ''),

0xa7: ('i32.wrap/i64', None, 0, 0, 0, ''),
0xa8: ('i32.trunc_s/f32', None, 0, 0, 0, ''),
0xa9: ('i32.trunc_u/f32', None, 0, 0, 0, ''),
0xaa: ('i32.trunc_s/f64', None, 0, 0, 0, ''),
Expand Down
13 changes: 9 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# API explorer
# Explorer
requests>=2.18.4

# API cfg graph
# CFG graphical view
graphviz>=0.8.3

# bytecode analytics
matplotlib>=2.2.2
numpy>=1.15.0

# ETH
# ethereum>=2.3.0
z3-solver>=4.5

# EOS
wasm>=1.1
# WASM / EOS
wasm>=1.1

0 comments on commit 568280e

Please sign in to comment.