| #!/usr/bin/env bcc-lua | |
| --[[ | |
| Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com> | |
| Licensed under the Apache License, Version 2.0 (the "License"); | |
| you may not use this file except in compliance with the License. | |
| You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| ]] | |
| -- Trace operations on keys matching given pattern in KyotoTycoon daemon. | |
| -- This can show you if certain keys were modified or read during the lifetime | |
| -- even if KT doesn't support this. It also shows how to attach to C++ mangled symbols. | |
| local ffi = require('ffi') | |
| local bpf = require('bpf') | |
| local S = require('syscall') | |
| local function help(err) | |
| print(string.format('%s [get|set] [key]', arg[0])) | |
| if err then print('error: '..err) end | |
| os.exit(1) | |
| end | |
| -- Accept the same format as ktremotemgr for clarity: <get|set> <key> | |
| local writeable, watch_key, klen = 'any', arg[2] or '*', 80 | |
| if arg[1] == 'get' then writeable = 0 | |
| elseif arg[1] == 'set' then writeable = 1 | |
| elseif arg[1] == '-h' or arg[1] == '--help' then help() | |
| elseif arg[1] and arg[1] ~= 'any' then | |
| help(string.format('bad cmd: "%s"', arg[1])) | |
| end | |
| if watch_key ~= '*' then klen = #watch_key end | |
| -- Find a good entrypoint that has both key and differentiates read/write in KT | |
| -- That is going to serve as an attachment point for BPF program | |
| -- ABI: bool accept(void *this, const char* kbuf, size_t ksiz, Visitor* visitor, bool writable) | |
| local key_type = string.format('char [%d]', klen) | |
| local probe = bpf.uprobe('/usr/local/bin/ktserver:kyotocabinet::StashDB::accept', | |
| function (ptregs) | |
| -- Watch either get/set or both | |
| if writeable ~= 'any' then | |
| if ptregs.parm5 ~= writeable then return end | |
| end | |
| local line = ffi.new(key_type) | |
| ffi.copy(line, ffi.cast('char *', ptregs.parm2)) | |
| -- Check if we're looking for specific key | |
| if watch_key ~= '*' then | |
| if ptregs.parm3 ~= klen then return false end | |
| if line ~= watch_key then return false end | |
| end | |
| print('%s write:%d\n', line, ptregs.parm5) | |
| end, false, -1, 0) | |
| -- User-space part of the program | |
| local ok, err = pcall(function() | |
| local log = bpf.tracelog() | |
| print(' TASK-PID CPU# TIMESTAMP FUNCTION') | |
| print(' | | | | |') | |
| while true do | |
| print(log:read()) | |
| end | |
| end) |