Tracer se kači preko JDWP-a (isti protokol kao debager). Aplikacija se ne menja i ostaje na svom JVM-u. Radi na portabilnom JDK 17, a cilj može biti Java 8.
cortiveo trace --app gjava.,db.,milos. --args 3 --tail 8000 --port 5005
cortiveo trace --app io.cortiveo. -- tools\jdk8\bin\java.exe -jar app.jarPrvi oblik se kači na JVM koji već sluša (server=y) — ne mora se ponovo prolaziti prijava. Drugi sam pokreće aplikaciju sa -agentlib:jdwp.
Ne postoji „javi mi samo prvi ulaz u ovu biblioteku". Postoje dva mehanizma i razlika je ogromna:
| mehanizam | kada košta | za šta |
|---|---|---|
MethodEntryRequest | na svakom ulazu u bilo koju metodu iz filtriranog skupa | tvoj kod, gde hoćeš sve |
BreakpointRequest | samo kad se pogodi | granica ka tuđem kodu, SQL, trigeri |
Zato tracer: tvoje pakete prati MethodEntry, a tuđi kod ne prati uopšte — granicu imenuješ.
| opcija | šta radi |
|---|---|
--app db.,milos. | šta se smatra „mojim kodom" (trigeri, lanci pozivalaca) |
--watch db.,milos.,-db.tehnobiro.generator3.DataHPrintBaseUtilities | za čim se stvarno prate pozivi; član sa - se izuzima i ne plaća |
--args 3 | vrednosti argumenata: Db.update("UPDATE invoice SET ..."...(51)) |
--call com.google.zxing.qrcode.QRCodeWriter.encode | granica ka biblioteci: koja metoda i sa čim, bez ulaska u nju |
--trigger actionPerformed,menuCall | šta otvara stablo (klik, ali i poziv bez GUI-ja) |
--tail 8000 | još toliko ms posle klika hvata SQL — ekran često puni podatke iz invokeLater |
--skip prefiks | klasa sa celim podstablom se ne ispisuje (ali se plaća) |
--depth N, --max N | koliko duboko i koliko redova |
--progress 2000 | red o napretku svake 2 s — bez njega tracer ćuti dok radi |
--scan | „apsolutno sve": i tuđi kod u stablo. Skupo, ali vidi sve |
Obrazac poklapanja: sa tačkom na kraju = paket (db.), bez tačke = tačna klasa i njene unutrašnje (gjava.data.GDataSource ne hvata GDataSourceLevel20).
... klik #2 12 s, 8432 poziva (+680), 2 sql, d11 GDataSource2d.recreateData
... klik #2 14 s, 8432 poziva, stoji (tudji kod ili baza), 2 sql, d11 GDataSource2d.recreateDataČita se: broj poziva raste → radi; stoji → čeka bazu ili biblioteku; red koji se ponavlja sa (xN) → petlja. Bez ovoga tracer ćuti upravo u najdužim delovima (tuđi kod, ispod --depth, posle --max), pa se ne zna da li se čeka uzalud.
| šta | brojka | zaključak |
|---|---|---|
| klik na stavku menija | 1017 poziva, 0 SQL, ~0,9 s | posao ide van klika, kroz invokeLater |
isti klik sa --args 3 | 1,6 s | vrednosti argumenata ≈ 1,2 ms po ispisanom redu |
menuCall (posao ekrana) | 58 104 poziva, 7 SQL, 94 s | od toga 67,7 s u jednom SELECT-u |
| klik na dugme u ekranu | 17 059 poziva, 3 SQL, 18 s | |
klik na štampu (--scan-oliki profil) | 175 012 poziva posle 47 minuta, bez kraja | amplifikacija metapodataka |
| raspodela redova stabla | 7627 gjava. : 64 db. | 99% cene je framework, 1% poslovna logika |
MethodEntry nad svim ne-JDK klasama, pa se plaćao svaki ulaz u MySQL drajver dok se čitao rezultat. Rešenje: SQL se hvata breakpoint-om na JDBC metodama (nađenim od java.sql.Connection/Statement kroz podinterfejse do implementacija) — drajver radi punom brzinom.GDataSourceLevel20.getColumn, GDataSource2d.getRowCount, columnExists — po koloni × po redu × po elementu štampe. To je cena arhitekture, ne greška tracera; tracer je samo prvi put izmerio.--app gjava.,db.,milos.
--watch db.,milos.,gjava.data.GDataSource,-db.tehnobiro.generator3.DataHPrintBaseUtilities
--call com.google.zxing.MultiFormatWriter.encode,com.google.zxing.qrcode.QRCodeWriter.encode
--args 3 --trigger actionPerformed,menuCall --tail 8000 --stack 8 --progress 2000Prevod: prati moju poslovnu logiku i ulaz u ekran, ne gradnju svakog pasusa i slike, a od biblioteka mi javi samo poziv QR koderu i njegove parametre.
Aplikacija već ima ulaz bez ekrana: gjava.interfaces.GMenuCallable
GError menuCall(GAppFrame pCallingFrame, String pParameters);Isti menuCall se vidi i u tragu klika (GDataSource.menuCall, GDataSource.java:308), pa je svaki posao koji se može odraditi klikom moguće odraditi i pozivom. Trag jednog klika sa --args pokazuje tačan poziv:
GDataSource.menuCall(GMainFrame@3389, "") GDataSource.java:308
Definition.edit(GMainFrame@3389, Object[0]) Definition.java:77
GDataSource.edit(GMainFrame@3389, Object[0]) GDataSource.java:328To je recept: pokaži klikom, izvrši pozivom.
cortiveo map snapshot.json --src <dirs> — stavka menija → klasa → izvorni fajl (na GTehnobiro 40/40, linije listenera iz bytecode-a 47/47).cortiveo launch/attach — ubacivanje agenta bez ručnog -javaagent.System.exit stack, JDBC log, i drag&drop fajla na polje tipa FileName.ExceptionTracer — svaki izuzetak, i uhvaćen, sa linijom.GResource.getRoot(Object) je nad Class objektom zvao Class.getResource, što je od Jave 9 ograničeno na modul java.base → null → mrtav <clinit>. Tri reda popravke i program radi na Javi 25 LTS.Could not initialize class je eho — uzrok je prvi ExceptionInInitializerError.