plympton 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ module Plympton
2
+ # Class responsible for parsing a YAML serialized block object
3
+ class Block
4
+ # YAML Entries
5
+ attr_accessor :startEA, :endEA, :numInstructions
6
+
7
+ # Defines the objects YAML tag
8
+ # @return [String] A string signifying the start of an object of this class
9
+ def to_yaml_type
10
+ "!fuzz.io,2011/Block"
11
+ end
12
+
13
+ # Convenience method for printing a block
14
+ # @return [String] A string representation of a block object
15
+ def to_s()
16
+ "#{self.class} (#{self.__id__}): Start Address: #{startEA}, End Address #{endEA}, No. of Instructions: #{numInstructions}"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module Plympton
2
+ # Class responsible for parsing a YAML serialized chunk object
3
+ class Chunk
4
+ # YAML entries
5
+ attr_accessor :startEA, :endEA, :blockList, :numBlocks
6
+
7
+ # Defines the objects YAML tag
8
+ # @return [String] A string signifying the start of an object of this class
9
+ def to_yaml_type
10
+ "!fuzz.io,2011/Chunk"
11
+ end
12
+
13
+ # Convenience method for printing a chunk
14
+ # @return [String] A string representation of a chunk object
15
+ def to_s()
16
+ "#{self.class} (#{self.__id__}):"
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,105 @@
1
+ module Plympton
2
+ # Class responsible for working with a disassembly
3
+ class Disassembly
4
+ attr_accessor :attributes
5
+ attr_accessor :expression
6
+
7
+ # Create an instance of the disassembly class
8
+ # Reads in a YAML serialized disassembly
9
+ # @param [String] Path to the YAML serialized disassembly
10
+ # @param [String] A mathematical expression for object evaluation
11
+ def initialize(yamlDisassembly, literalExpression="")
12
+
13
+ # Check for existence of the file
14
+ if(!File.exists?(yamlDisassembly)) then
15
+ return(nil)
16
+ end
17
+
18
+ # Unserialize the YAML disassembly
19
+ @attributes = YAML.load(File.open(yamlDisassembly))
20
+ @attributes.setup()
21
+
22
+ # Create an instance of the expression
23
+ initialize_solver(literalExpression)
24
+ end
25
+
26
+ # Wrapper function for the solver's evaluate function
27
+ # After expression evaluation it resets for another turn
28
+ # @param [BigDecimal] Test case runtime, defaults to zero
29
+ # @return [BigDecimal] The results of the evaluated expression
30
+ def evaluate(runtime="0")
31
+ @attributes.runtime = BigDecimal(runtime)
32
+ result = @expression.evaluate()
33
+ initialize_solver(@expression.expressionCache)
34
+
35
+ # Catch Not A Number or Infinite cases
36
+ if(result.nan?() or result.infinite?()) then
37
+ result = BigDecimal("0")
38
+ end
39
+
40
+ return(result)
41
+ end
42
+
43
+ # Function to process hit tracing recorded by Valgrind tools (rufus and callgrind)
44
+ # @param [String] Path to a valgrind trace file
45
+ def valgrind_coverage(valgrindTrace)
46
+
47
+ # Open the valgrind xml trace file
48
+ xmlFile = File.open(valgrindTrace, "r")
49
+ xmlDoc = Nokogiri::XML(xmlFile)
50
+
51
+ # Delete any previous hit traces
52
+ @attributes.functionHitTrace.clear()
53
+
54
+ # Parse all the function hits
55
+ xmlDoc.xpath("//hit").each do |hit|
56
+ functionOffset = hit.search("offset").first().inner_text()
57
+ #puts "Function offset hit: #{functionOffset}"
58
+ if(@attributes.functionHash.has_key?(functionOffset)) then
59
+ #puts "Function offset found: #{functionOffset}"
60
+ if(!@attributes.functionHitTrace.has_key?(functionOffset)) then
61
+ @attributes.functionHitTrace[functionOffset] = [1]
62
+ else
63
+ @attributes.functionHitTrace[functionOffset][0] = @attributes.functionHitTrace[functionOffset][0] + 1
64
+ end
65
+
66
+ # Parse all the functions this function called
67
+ hit.xpath("callee").each do |callee|
68
+ calleeOffset = callee.search("offset").first().inner_text()
69
+ numberOfCalls = callee.search("numberOfCalls").first().inner_text().to_i()
70
+ if(@attributes.functionHash.has_key?(calleeOffset)) then
71
+ if(!@attributes.functionHitTrace.has_key?(functionOffset)) then
72
+ @attributes.functionHitTrace[functionOffset] = [numberOfCalls]
73
+ else
74
+ @attributes.functionHitTrace[functionOffset][0] = @attributes.functionHitTrace[functionOffset][0] + numberOfCalls
75
+ end
76
+
77
+ #puts "Function offset: #{functionOffset} -> #{calleeOffset}"
78
+ # Increment the number of transitions for a state
79
+ @attributes.functionHash[functionOffset].numTransitions += BigDecimal("#{numberOfCalls}")
80
+
81
+ # Update the transition matrix
82
+ @attributes.transitionMatrix[@attributes.functionHash[functionOffset].markovIdx, @attributes.functionHash[calleeOffset].markovIdx] = @attributes.transitionMatrix[@attributes.functionHash[functionOffset].markovIdx, @attributes.functionHash[calleeOffset].markovIdx] + BigDecimal("#{numberOfCalls}")
83
+
84
+ # Keep track of call trace and number of times called
85
+ @attributes.trace << "#{@attributes.functionHash[functionOffset].markovIdx}:#{@attributes.functionHash[calleeOffset].markovIdx}:#{numberOfCalls}"
86
+ end
87
+ end # end callee xpath
88
+ end # end function hash check for hit function
89
+ end # end hit xpath
90
+
91
+ # Cleanup open file
92
+ xmlFile.close()
93
+ end
94
+
95
+ # Parses a mathematical expression and setups for function evaluation
96
+ # @param [String] A mathematical expression for object evaluation
97
+ def initialize_solver(literalExpression)
98
+ # Allocate an expression to solve
99
+ @expression = Solver::Parser.new(Solver::Lexer.new(literalExpression))
100
+ @expression.expressionCache = literalExpression
101
+ @expression.objectCache = @attributes
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,31 @@
1
+ module Plympton
2
+ # Class responsible for parsing a YAML serialized function object
3
+ class Function
4
+ # YAML Entries
5
+ attr_accessor :name, :startAddress, :endAddress, :savedRegSize, :localVarSize, :argSize, :frameSize, :numArgs, :numLocalVars, :isImport, :numChunks, :chunkList, :cyclomaticComplexity
6
+
7
+ attr_accessor :numInstructions
8
+ attr_accessor :numTransitions, :markovIdx
9
+
10
+ # Defines the objects YAML tag
11
+ # @return [String] A string signifying the start of an object of this class
12
+ def to_yaml_type
13
+ "!fuzz.io,2011/Function"
14
+ end
15
+
16
+ def set_total_number_of_instructions()
17
+ @numInstructions = 0
18
+ @chunkList.each do |chunk|
19
+ chunk.blockList.each do |block|
20
+ @numInstructions = @numInstructions + block.numInstructions
21
+ end
22
+ end
23
+ end
24
+
25
+ # Convenience method for printing a function
26
+ # @return [String] A string representation of a function object
27
+ def to_s()
28
+ "#{self.class} (#{self.__id__}):#{name}"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,59 @@
1
+ class NMatrix
2
+ # Class Methods
3
+
4
+ # Create a new square matrix of BigDecimal 0 objects
5
+ # @param [Fixnum] Number of rows and columns
6
+ # def self.square(dimension)
7
+ # matrix = PlymptonMatrix.object(dimension, dimension)
8
+ # matrix.fill!(BigDecimal("0"))
9
+ # puts matrix.class()
10
+ # return(matrix)
11
+ # end
12
+
13
+ def investigate()
14
+ dimensions = shape()
15
+ rowDimension = dimensions[0]
16
+ colDimension = dimensions[1]
17
+ puts ""
18
+ for row in 0...rowDimension
19
+ for column in 0...colDimension
20
+ print " %10f" % self[row, column].to_s('F')
21
+ end
22
+ puts""
23
+ end
24
+ end
25
+
26
+ # Allocate a matrix of BigDecimal zeros
27
+ # @param [Fixnum] Size of the square matrix n x n
28
+ # @returns [PlymptonMatrix] An n x n matrix with every entry a BigDecimal("0")
29
+ # def self.zero(size)
30
+ # PlymptonMatrix.build(size) do
31
+ # BigDecimal("0")
32
+ # end
33
+ # end
34
+
35
+ # Instance Methods
36
+ # Prints the string representation of a Matrix object
37
+ # @params [Hash] Hash of functions defined by an object
38
+ # def to_s()
39
+ # rowSize = row_size()
40
+ # colSize = column_size()
41
+ #
42
+ # # Print Matrix Header
43
+ # print "%12s" % ""
44
+ # for colIdx in 0...colSize do
45
+ # print "%10d" % colIdx
46
+ # end
47
+ # spacer = "-" * 100
48
+ # print "\n %s\n" % spacer
49
+ #
50
+ # for rowIdx in 0...rowSize do
51
+ # print "%10d |" % rowIdx
52
+ # for colIdx in 0...colSize do
53
+ # print" %10f" % self[rowIdx, colIdx].to_s('10F')
54
+ # end
55
+ # print(" |\n")
56
+ # end
57
+ # nil
58
+ # end
59
+ end
@@ -0,0 +1,153 @@
1
+ module Plympton
2
+ # Class responsible for parsing a YAML serialized object
3
+ class Object
4
+ # YAML entries
5
+ attr_accessor :textSegmentStart, :textSegmentEnd, :functionList, :importList, :numFunctions, :numImports, :numBlocks, :name
6
+
7
+ # Class Attributes not imported via YAML
8
+ attr_accessor :runtime
9
+ attr_accessor :functionHash
10
+ attr_accessor :functionHitTrace
11
+ attr_accessor :transitionMatrix
12
+ attr_accessor :trace
13
+
14
+ # Defines the objects YAML tag
15
+ # @return [String] A string signifying the start of an object of this class
16
+ def to_yaml_type
17
+ "!fuzz.io,2011/Object"
18
+ end
19
+
20
+ # Creates hashes for function lookup and cleans up data imported via YAML::load
21
+ def setup()
22
+ # Housekeeping on library name
23
+ @name.chomp!()
24
+
25
+ # Allocate a hash for function hit tracing
26
+ @functionHitTrace = Hash.new()
27
+
28
+ # Create a function hash for lookups
29
+ init_function_hash()
30
+
31
+ # Add in the markov function (any function not defined by a shared library)
32
+ markovFunction = Plympton::Function.new()
33
+ markovFunction.markovIdx = 0
34
+ markovFunction.numTransitions = BigDecimal("0")
35
+ @functionHash["-1"] = markovFunction
36
+
37
+ # Precompute number of instructions per function and set markovIdx
38
+ markovIdx = 1
39
+ @functionList.each do |function|
40
+ function.set_total_number_of_instructions()
41
+ function.markovIdx = markovIdx
42
+ function.numTransitions = BigDecimal("0")
43
+ markovIdx += 1
44
+ end
45
+
46
+ # Allocate transition matrix (# functions + special state 0)
47
+ # Transition matrix persists across test case runs
48
+ # dimension = @functionHash.size() + 1
49
+ dimension = @functionHash.size()
50
+ @transitionMatrix = NMatrix.object(dimension, dimension).fill!(BigDecimal("0"))
51
+
52
+ # Allocate a trace for Markov chains
53
+ @trace = Array.new()
54
+ end
55
+
56
+ #
57
+ # @param [String] Base variable name to create a sum and average function
58
+ # @param [Symbol] Associated instance variable to calculate the sum and average
59
+ def self.define_sum_avg(variable, attribute)
60
+ sum_function = variable + "s"
61
+ define_method(sum_function.to_sym()) do
62
+ result = BigDecimal("0")
63
+ if(@functionHitTrace !=nil and @functionHitTrace.length() > 0) then
64
+ # Iterate through the function's hit hash
65
+ @functionHitTrace.each do |offset, numberTimesExecuted|
66
+ result = result + BigDecimal("#{@functionHash[offset].send(attribute.to_sym())}") * BigDecimal("#{numberTimesExecuted[0]}")
67
+ end
68
+ end
69
+ return(result)
70
+ end
71
+
72
+ # Define the average function
73
+ avg_function = variable + "a"
74
+ define_method(avg_function.to_sym()) do
75
+ # Catch if the functionHitTrace length is zero
76
+ numFunctionsExecuted = BigDecimal("0")
77
+ @functionHitTrace.each do |offset, numberTimesExecuted|
78
+ numFunctionsExecuted = numFunctionsExecuted + BigDecimal("#{numberTimesExecuted[0]}")
79
+ end
80
+
81
+ send(sum_function.to_sym())/numFunctionsExecuted
82
+ end
83
+ end
84
+
85
+ # Define all the sum and average functions
86
+ {"A" => :numArgs, "B" => :numLocalVars, "C" => :argSize, "D" => :localVarSize,"E" => :numInstructions,"F" => :frameSize,"G" => :cyclomaticComplexity}.each do |variable, attribute|
87
+ define_sum_avg(variable, attribute)
88
+ end
89
+
90
+ # Function to calculate execution path uniqueness:
91
+ # http://www.cs.ucf.edu/~czou/research/EvolutionaryInputCrafting-ACSAC07.pdf
92
+ # Had to scale due to overflow: log 1/product(pi) = log(productpi)-1 = -log(productpi) = - summation log(pi)
93
+ # @return [BigDecimal] The steady state transition probability
94
+ def U()
95
+ dimensions = @transitionMatrix.shape()
96
+ rowDimension = dimensions[0]
97
+ colDimension = dimensions[1]
98
+ pMatrix = NMatrix.object(rowDimension, colDimension).fill!(BigDecimal("0"))
99
+
100
+ # Calculate the new transition probabilities
101
+ @functionHash.each_value do |function|
102
+ rowIdx = function.markovIdx
103
+ # puts "#{function.startAddress}: #{function.numTransitions.to_s("F")}"
104
+ for column in 0...colDimension
105
+ if(function.numTransitions != BigDecimal("0")) then
106
+ pMatrix[rowIdx,column] = @transitionMatrix[rowIdx,column]/function.numTransitions
107
+ end
108
+ end
109
+ end
110
+
111
+ # Calculate the statistic for function path uniqueness
112
+ pathUniqueness = BigDecimal("0")
113
+ @trace.each do |callTrace|
114
+ traceArr = callTrace.split(":")
115
+ traceArr[2].to_i.times do
116
+ #pathUniqueness += BigMath.log(pMatrix[traceArr[0].to_i(), traceArr[1].to_i()], 6)
117
+ pathUniqueness += Math.log(pMatrix[traceArr[0].to_i(), traceArr[1].to_i()].to_f())
118
+ end
119
+ # puts "#{pMatrix[traceArr[0].to_i(), traceArr[1].to_i()].to_f()}"
120
+ end
121
+
122
+ #puts "Path Uniqueness is: #{pathUniqueness.to_s("F")}"
123
+ pathUniqueness = BigDecimal(pathUniqueness.abs.to_s())
124
+ #puts "Path Uniqueness is: #{pathUniqueness.to_s("F")}"
125
+ @trace.clear()
126
+ return(pathUniqueness)
127
+ end
128
+
129
+ # Function to calculate the function coverage of a test case run
130
+ # Total number of unique functions/Total number of unique functions executed
131
+ # @return [BigDecimal] The percentage
132
+ def V()
133
+ functionCoverage = @functionHitTrace.length()/@numFunctions
134
+ return(BigDecimal(functionCoverage.to_s()))
135
+ end
136
+
137
+ private
138
+
139
+ # Convenience method for creating a lookup hash for functions based on hex address
140
+ def init_function_hash()
141
+ @functionHash = Hash.new()
142
+ @functionList.each do |function|
143
+ @functionHash[function.startAddress] = function
144
+ end
145
+ end
146
+
147
+ # Convenience method for printing a chromosome
148
+ # @return [String] A string representation of the chromosome object
149
+ def to_s()
150
+ "#{self.class} (#{self.__id__}):\n#{name}\t#{textSegmentStart}\t#{textSegmentEnd}\t#{numFunctions}\t#{numImports}\t#{numBlocks}"
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,118 @@
1
+ grammar Solver;
2
+
3
+ options {
4
+ language = Ruby;
5
+ }
6
+
7
+ tokens {
8
+ PLUS = '+';
9
+ MINUS = '-';
10
+ MULT = '*';
11
+ DIV = '/';
12
+ MOD = '%';
13
+ EXP = '^';
14
+ LPAREN = '(';
15
+ RPAREN = ')';
16
+ }
17
+
18
+ @header {
19
+ require 'bigdecimal/math'
20
+ }
21
+
22
+ @members {
23
+ attr_accessor :expressionCache
24
+ attr_accessor :objectCache
25
+
26
+ # Recovery function handling errors
27
+ def recover( error = $! )
28
+ puts "Parser Recovering: #{error}"
29
+ exit(1)
30
+ end
31
+
32
+ # Reporting function handling errors
33
+ def report_error( error = $! )
34
+ puts "Parser Reporting: #{error}"
35
+ exit(1)
36
+ end
37
+ }
38
+
39
+
40
+ @lexer::members {
41
+ # Recovery function handling errors
42
+ def recover( error = $! )
43
+ puts "Lexer Recovering: #{error}"
44
+ exit(1)
45
+ end
46
+
47
+ # Reporting function handling errors
48
+ def report_error( error = $! )
49
+ puts "Lexer Reporting: #{error}"
50
+ exit(1)
51
+ end
52
+ }
53
+
54
+ /*------------------------------------------------------------------
55
+ * PARSER RULES
56
+ *------------------------------------------------------------------*/
57
+ evaluate returns [result]
58
+ : r=expression {$result = $r.result }
59
+ ;
60
+ expression returns [result]
61
+ : r=mult
62
+ ( '+' r2=mult { $r.result += $r2.result }
63
+ | '-' r2=mult { $r.result -= $r2.result }
64
+ )* { $result = $r.result }
65
+ ;
66
+
67
+ mult returns [result]
68
+ : r=log
69
+ ( '*' r2=log { $r.result *= $r2.result }
70
+ | '/' r2=log { $r.result /= $r2.result }
71
+ )* { $result = $r.result };
72
+
73
+ log returns [result]
74
+ : 'ln' r=exp { $result = BigMath.log(r, 2)}
75
+ | r=exp { $result = $r.result }
76
+ ;
77
+
78
+ exp returns [result]
79
+ : r=atom ( '^' r2=atom { $r.result **= $r2.result.to_i() } )? { $result = $r.result }
80
+ ;
81
+
82
+ atom returns [result]
83
+ : (a=INT {$result = BigDecimal($a.text)}
84
+ | a=FLOAT {$result = BigDecimal($a.text)}
85
+ | LPAREN r=expression { $result = $r.result } RPAREN
86
+ | a=MODVAR {
87
+ $result = @objectCache.send($a.text)
88
+ }
89
+ | a=UNMODVAR {
90
+ case $a.text
91
+ when 'R'
92
+ $result = @objectCache.runtime
93
+ when 'U'
94
+ $result = @objectCache.send($a.text)
95
+ when 'V'
96
+ $result = @objectCache.send($a.text)
97
+ else
98
+ $result = BigDecimal("0")
99
+ end
100
+ }
101
+ )
102
+ ;
103
+
104
+ /*------------------------------------------------------------------
105
+ * LEXER RULES
106
+ *------------------------------------------------------------------*/
107
+ INT : (DIGIT)+ ;
108
+ FLOAT : INT '.' INT;
109
+ MODVAR : 'A'..'G'(MODIFIER);
110
+ UNMODVAR : ( 'R' | 'U' | 'V' );
111
+ WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ { $channel = HIDDEN; };
112
+
113
+ /*------------------------------------------------------------------
114
+ * Fragment RULES (Only used as part of another Lexer Rule)
115
+ *------------------------------------------------------------------*/
116
+
117
+ fragment DIGIT : '0'..'9';
118
+ fragment MODIFIER: 'a'|'s';