Skip to content

Commit

Permalink
impl: Implement to resolve call hieralchy.
Browse files Browse the repository at this point in the history
tweaks:  Change to warn message instead of raise error when unmatching program counter.
tweaks: Disable some of noisy warnings.
  • Loading branch information
nakajo2011 committed May 6, 2019
1 parent 342df52 commit 4743dfb
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 71 deletions.
118 changes: 118 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"eslint-plugin-promise": "^3.8.0",
"eslint-plugin-react": "^7.9.1",
"eslint-plugin-standard": "^3.1.0",
"mocha": "^6.1.4"
"mocha": "^6.1.4",
"sinon": "^7.3.2"
}
}
58 changes: 34 additions & 24 deletions src/coverage_subprovider.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const inherits = require('util').inherits
const promisify = require('util').promisify
const { separateTraceLogs } = require('./tracelog_utils')
const Subprovider = require('web3-provider-engine/subproviders/subprovider')
const TraceCollector = require('./trace_collector')
const TruffleArtifactResolver = require('./truffle_artifacts_resolver')
Expand Down Expand Up @@ -28,7 +29,7 @@ function CoverageSubprovider(provider, jsonGlob = null) {
}
this.provider = provider
this.resolver = jsonGlob ? new TruffleArtifactResolver(jsonGlob) : new TruffleArtifactResolver()
this.collecotr = new TraceCollector()
this.collector = new TraceCollector()
}

function debugTraceTransaction(txhash, cb) {
Expand All @@ -37,8 +38,8 @@ function debugTraceTransaction(txhash, cb) {
method: 'debug_traceTransaction',
params: [txhash, {
disableStorage: true,
disableMemory: true,
disableStack: true
disableMemory: true
// disableStack: true
}]
}, cb)
}
Expand Down Expand Up @@ -84,44 +85,53 @@ CoverageSubprovider.prototype._getCode = promisify(getCode)
CoverageSubprovider.prototype.handleRequest = function(payload, next, end) {
const self = this

function findContract(contractAddress) {
return async() => {
if (!self.resolver.exists(contractAddress)) {
const response = await self._getCode(contractAddress)
if (response.result != null) { self.resolver.findByCodeHash(response.result, contractAddress) }
}
}
}

function getTraceAndCollect(contractAddress) {
return function(txHash) {
self._debugTraceTransaction(txHash)
.then(res => {
self.collecotr.add(contractAddress, res.result)
return self._getCode(contractAddress)
})
.then(getCodeResponse => {
if (!self.resolver.exists(contractAddress)) {
self.resolver.findByCodeHash(getCodeResponse.result, contractAddress)
}
})
return async function(txHash) {
const response = await self._debugTraceTransaction(txHash)
// console.log(JSON.stringify(res))
const separated = separateTraceLogs(response.result.structLogs)
self.collector.add(contractAddress, separated[0].traceLogs)
await findContract(contractAddress)()
for (let i = 1; i < separated.length; i++) {
const trace = separated[i]
self.collector.recordFunctionCall({ to: trace.address, data: trace.functionId })
self.collector.add(trace.address, trace.traceLogs)
await findContract(trace.address)()
}
}
}

let traceFunc = function() {}
let traceFunc = async() => {}
switch (payload.method) {
case 'eth_call':
self.collecotr.recordFunctionCall(payload.params[0])
self.collector.recordFunctionCall(payload.params[0])
traceFunc = findContract(payload.params[0].to)
break

case 'eth_sendTransaction':
const param = payload.params[0]
if (!isNewContract(param.to) && param.data.length > 4) {
self.collecotr.recordFunctionCall(param)
// next(getTraceAndCollect(param.to))
// } else {
// next(null)
self.collector.recordFunctionCall(param)
traceFunc = getTraceAndCollect(param.to)
}
break
}

this.provider.sendAsync(payload, function(err, response) {
if (err) return end(err)
traceFunc(response.result)
if (response.error) return end(new Error(response.error.message))
end(null, response.result)
traceFunc(response.result).then(() => {
if (response.error) return end(response.error.message)
end(null, response.result)
})
})
}

Expand All @@ -131,6 +141,6 @@ CoverageSubprovider.prototype.start = function() {

CoverageSubprovider.prototype.stop = function() {
const self = this
const coverager = new Coverager(self.resolver, self.collecotr)
const coverager = new Coverager(self.resolver, self.collector)
coverager.report()
}
23 changes: 13 additions & 10 deletions src/coverager.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,28 @@ class Coverager {
report() {
const self = this
const matchingDatas = self._matching()
console.log('\n\n')
console.log(colors.bold.underline('Coverage report.'))

let resultStr = '\n\n' + colors.bold.underline('Coverage report.\n')
Object.keys(matchingDatas).forEach((contractName, index) => {
console.log(`(${index + 1}) ${contractName}`)
resultStr += `(${index + 1}) ${contractName}\n`
const data = matchingDatas[contractName]
const numInstructions = data.contract.compiler.name === 'solc' ? null : data.contract.deployedBytecode.length / 2
this.reportUsedOpecodes(data.contract.deployedBytecode, data.traces, numInstructions)
this.reportMethodCalled(data.contract.functions, data.funcCalls)
resultStr += this.reportUsedOpecodes(data.contract.deployedBytecode, data.traces, numInstructions)
resultStr += this.reportMethodCalled(data.contract.functions, data.funcCalls)
resultStr += '\n\n'
})
console.log(resultStr)
}

reportUsedOpecodes(bytecodes, structLogs, numInstructions) {
const matching = matchUsedOpecodes(bytecodes, structLogs, numInstructions)
const usedTotally = countUsed(matching)
console.log(`deployedBytecode coverage: ${Number((usedTotally * 100) / matching.length).toFixed(2)}% (${usedTotally}/${matching.length})`)
console.log('')
return `deployedBytecode coverage: ${Number((usedTotally * 100) / matching.length).toFixed(2)}% (${usedTotally}/${matching.length})\n`
}

reportMethodCalled(functions, calls) {
const table = new Table({ head: ['method', 'call count'], colWidths: [20, 20], style: { head: ['bold'] } })
const masLength = Math.max(...Object.keys(functions).map(sig => sig.length), 20)
const table = new Table({ head: ['method', 'call count'], colWidths: [masLength + 2, 20], style: { head: ['bold'] } })
let calledCount = 0
let functionCount = 0
Object.entries(matchCalledFunction(functions, calls)).forEach(item => {
Expand All @@ -73,8 +75,9 @@ class Coverager {
table.push(item)
functionCount++
})
console.log(`method coverage : ${Number((calledCount * 100) / functionCount).toFixed(2)}% (${calledCount}/${functionCount})`)
console.log(table.toString())
let res = `method coverage : ${Number((calledCount * 100) / functionCount).toFixed(2)}% (${calledCount}/${functionCount})\n`
res += table.toString()
return res
}
}

Expand Down
13 changes: 8 additions & 5 deletions src/match_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ function matchUsedOpecodes(bytecodes, structLogs, numInstructions) {
const pc2IndexMap = getPcToInstructionIndexMapping(opcodeStructs)
const res = JSON.parse(JSON.stringify(opcodeStructs))
structLogs.forEach(log => {
const sm = res[pc2IndexMap[log.pc]]
if (sm === undefined) {
throw new Error(`unknown program counter: ${log.pc}, details:\n${JSON.stringify(log, null, ' ')}`)
if (log && log.pc !== undefined) {
const sm = res[pc2IndexMap[log.pc]]
if (sm === undefined) {
console.warn(`unknown program counter: ${log.pc}, depth=${log.depth}`)//, details:\n${JSON.stringify(log, null, ' ')}`)
} else {
sm.vmTraces = sm.vmTraces || 0
sm.vmTraces++
}
}
sm.vmTraces = sm.vmTraces || 0
sm.vmTraces++
})
return res
}
Expand Down
4 changes: 2 additions & 2 deletions src/trace_collector.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class TraceCollector {
// deployed transaction is not function call message.
return
}
if (bytecodes.length < 4) {
console.warn(`Not Contract call message. ${JSON.stringify(txRequestParams)}`)
if (bytecodes.length < 8) {
// console.warn(`Not Contract call message. ${JSON.stringify(txRequestParams)}`)
return
}
if (this.funcCallMap[address] === undefined) {
Expand Down
Loading

0 comments on commit 4743dfb

Please sign in to comment.