plympton 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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';