') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Agent Instrumentation · codedx/bytefrog Wiki · GitHub
Skip to content

Agent Instrumentation

Robert Ferris edited this page Apr 18, 2014 · 6 revisions

Bytefrog's Java agent instruments the code being traced, injecting function calls to trace method entries, method exits, thrown exceptions, and uncaught exceptions. We utilize ASM for facilitating the instrumentation process.

Data Collection

A public singleton Trace (com.secdec.bytefrog.agent.trace.Trace) exists. Method calls on this object are injected during the instrumentation process. Every trace method takes the method metadata (the fully qualified name of the method, the full JVM signature, and any access flags) as well as any specific data needed for the event.

Method Entries

Method entries are instrumented by inserting a call to Trace.methodEntry at the beginning of every traced method. This records the method entry along with the method metadata.

Thrown Exceptions

Thrown exceptions are recorded by inserting a call to Trace.methodThrow prior to any throw instructions. This records the method metadata, the thrown exception, and the line number (if available).

Method Exits

Execution may leave a method in one of two ways: normal return, and throwing an exception.

Normal Method Return

Normal method returns are instrumented by inserting a call to Trace.methodExit before any return call (including one implicitly at the end of the method). This records the method exit along with the method metadata and the line number (if available).

Uncaught Exception

Uncaught exceptions are detected as they leave a method. This is accomplished by wrapping the entire method body in a try block. A generic catch block is then inserted, calling Trace.methodBubble and re-throwing the exception to allow it to continue up the execution stack. For uncaught exceptions, the exception and method metadata are recorded.

Example

As an example, consider the following Java method:

publicvoidfoo() {
if (badCondition) thrownewException("failed");
return4;
}

After Bytefrog instrumentation, the method will effectively become:

publicvoidfoo() {
try {
com.secdec.bytefrog.agent.trace.Trace.methodEntry("com/foo/bar.foo;1;()V");
if (badCondition) {
Exceptionex = newException("failed");
com.secdec.bytefrog.agent.trace.Trace.methodThrow(ex, "com/foo/bar.foo;1;()V", 2);
throwex;
}
com.secdec.bytefrog.agent.trace.Trace.methodExit("com/foo/bar.foo;1;()V", 3);
return4;
} catch (Throwablet) {
com.secdec.bytefrog.agent.trace.Trace.methodBubble(t, "com/foo/bar.foo;1;()V");
throwt;
}
}

This instrumentation happens dynamically as the class file is loaded by the JVM. As such, these changes exist only in memory, and only for as long as that JVM instances is executing. The original code remains unmodified.

Clone this wiki locally