تتبع البيانات المتدفقة عبر برامج Java، واكتساب فهم جديد في لمحة.
FlowTracker هو وكيل Java يتتبع كيفية قراءة البرنامج للبيانات ومعالجتها وكتابتها. من خلال مشاهدة تشغيل البرنامج، يمكنه إظهار ما حدث من عمليات إدخال وإخراج للملفات والشبكة، ولكن الأهم من ذلك هو ربط مدخلاته ومخرجاته لإظهار مصدر مخرجاته. يساعدك هذا على فهم معنى مخرجات أي برنامج Java والسبب الذي جعله يكتبها.
Spring PetClinic هو تطبيق تجريبي لإطار عمل Spring. ولإثبات قدرات FlowTracker، نسمح له بمراقبة PetClinic وهو يتعامل مع طلب HTTP ويولد صفحة HTML استنادًا إلى قالب وبيانات من قاعدة بيانات. يمكنك استخدام هذا العرض التوضيحي في متصفحك، دون تثبيت أي شيء. افتح”https://flowtracker-demo.coekie.com/petclinic/#Serversocket/*/%2F127.0.0.1%3A*/Write” rel=”nofollow”>عرض توضيحي لـ FlowTracker PetClinicأو شاهد الفيديو أدناه.
عيادة الحيوانات الأليفة.mp4
يمكنك رؤية استجابة HTTP التي شاهدها FlowTracker عندما أرسلتها PetClinic عبر الشبكة. انقر على جزء من محتويات استجابة HTTP لمعرفة مصدر هذا الجزء في العرض السفلي. يمكنك تحديد مصدر/مدخل أو مصدر/مخرج آخر متتبع في الشجرة على اليسار (أو الزر الأيسر السفلي على الهاتف المحمول).
عند استكشاف استجابة HTTP هذه، ننتقل عبر طبقات متعددة من مجموعة البرامج:
- التعامل مع HTTP يُظهر FlowTracker الكود الذي أنتج ما تم إخراجه. انقر على “HTTP/1.1” أو رؤوس HTTP. ترى أن هذا الجزء من الاستجابة تم إنشاؤه بواسطة Apache Coyote (الفئات الموجودة في
org.apache.coyote
الحزمة، والتي تشير إليك بالضبط من أين جاء كل رأس. - قوالب أوراق الزعتر يُظهر FlowTracker كيف تتوافق المدخلات التي يقرأها البرنامج (قوالب HTML) مع المخرجاتانقر فوق اسم علامة HTML، مثل “html” أو “head”.أنت ترى
layout.html
الملف الذي يأتي منه هذا الجزء من صفحة HTML. إذا قمت بالنقر فوقlayout.html
، ثم على الملونة+
الزر الموجود في الأسفل، ثم سيتم تمييز كل شيء صادر عن هذا الملف بنفس اللون. عند التمرير لأسفل، ستلاحظ أن جزءًا من الاستجابة يأتي من ملف مختلف،ownerDetails.html
. انقر على<
أو>
للتأكد من أن هذه الأحرف قد تمت كتابتها بواسطة مكتبة قوالب Thymeleaf. - قاعدة البياناتتحتوي صفحة HTML على جدول يحتوي على معلومات تأتي من قاعدة البيانات. النقر فوق
George
لا يوضح الجدول أن هذه القيمة جاءت من قاعدة البيانات فحسب، بل إنه يمضي إلى أبعد من ذلك: حيث يتتبعها حتى يصل إلى البرنامج النصي SQL الذي أدخل هذه القيمة في قاعدة البيانات في المقام الأول.
في هذا العرض التوضيحي، يعمل التتبع حتى نص SQL لأنه كان يستخدم قاعدة بيانات في الذاكرة. لم يخرج محتوى قاعدة البيانات من JVM مطلقًا، لذا يمكن لـ FlowTracker تتبعه بالكامل. عندما نشغل نفس العرض التوضيحي ولكن باستخدام قاعدة بيانات MySQL، فإننا نتتبع هذه القيم حتى اتصال قاعدة البيانات: نرى استعلام SQL المرسل مسبقًا لإنتاجها، وتفاصيل حول كيفية تعامل برنامج تشغيل MySQL JDBC مع قاعدة البيانات. انظر"https://flowtracker-demo.coekie.com/petclinic-mysql/#Serversocket/*/%2F127.0.0.1%3A*/Write" rel="nofollow">عرض توضيحي لـ FlowTracker PetClinic mysqlلاحظ أن FlowTracker يعترض المحتويات التي تم فك تشفيرها والتي تم إرسالها عبر اتصال SSL إلى قاعدة البيانات.
يعد عرض Spring PetClinic التجريبي هذا مجرد مثال. لا يعتمد FlowTracker على استخدام تطبيقك لأي إطار عمل أو مكتبة معينة.
عرض توضيحي آخر، يوضح كيف يساعدك FlowTracker من خلال مشاهدة مُجمِّع Java على فهم تنسيق ملف الفئة المُولَّد والرمز الثنائي الموجود فيه:"https://flowtracker-demo.coekie.com/javac/#Files/home/coekie/flowtracker-demo/HelloWorld.class" rel="nofollow">javac تجريبي,"https://github.com/user-attachments/assets/5884c8fd-342b-471e-b13d-a2fe7219e8e6">فيديو.
تحذير: في حالته الحالية، يعتبر FlowTracker أقرب إلى إثبات المفهوم منه إلى الجاهزية للإنتاج. لقد أثبت أنه يعمل بشكل جيد على عدد من برامج المثال، لكنه لن يعمل بشكل جيد مع كل شيء، فقد تختلف النتائج. كما يجب أن تدرك أنه يضيف الكثير من النفقات العامة، مما يجعل تشغيل البرامج أبطأ كثيرًا.
قم بتنزيل ملف jar الخاص بوكيل FlowTracker من"https://github.com/coekie/flowtracker/releases">صفحات إصدارات Github (flowtracker-*.jar
تحت "Assets"). أضف العميل إلى سطر أوامر Java الخاص بك: -javaagent:path/to/flowtracker.jar
. قم بتعطيل بعض تحسينات JVM التي تعطل flowtracker عن طريق إضافة إخراج java -jar flowtracker.jar jvmopts
إلى سطر الأوامر. بشكل افتراضي، يبدأ FlowTracker خادم الويب على المنفذ 8011، لذا افتح"http://localhost:8011/" rel="nofollow">http://localhost:8011/ في متصفحك.
للحصول على تعليمات أكثر تفصيلاً، بما في ذلك خيارات التكوين، راجع"http://github.com/coekie/flowtracker/blob/master/USAGE.md">الاستخدام.md.
"-1" دير="auto">كيف يعمل داخليا
"-1" دير="auto">النسخة القصيرة
FlowTracker هو وكيل للقياس. يقوم الوكيل بحقن الكود الخاص به في ملفات الفئة (الكود الثنائي) عندما يقوم JVM بتحميلها. يحافظ هذا الكود على تعيين البيانات الموجودة في الذاكرة إلى مصدرها، بينما يقوم البرنامج بقراءة البيانات وتمريرها وكتابتها. ينصب التركيز على تتبع البيانات النصية والثنائية (مثل السلاسل والمصفوفات الثنائية)، وليس على البيانات الرقمية أو المنظمة أو المحسوبة.
تم تحقيق ذلك من خلال الجمع بين:
- استبدال بعض المكالمات إلى طرق JDK بمكالمات إلى إصدار FlowTracker من تلك الطرق.
- حقن الكود في الأماكن الرئيسية في JDK، وذلك في الغالب لتتبع المدخلات والمخرجات.
- تحليل تدفق البيانات والأجهزة الأكثر عمقًا داخل الأساليب لتتبع المتغيرات والقيم المحلية على المكدس.
- إضافة التعليمات البرمجية قبل وبعد استدعاءات الطريقة، وفي بداية ونهاية الطرق المستدعاة، لتتبع وسيطات الطريقة وقيم الإرجاع باستخدام ThreadLocals.
"-1" دير="auto">نموذج البيانات: المتتبعات
الفئات والمفاهيم الأساسية لنموذج بيانات FlowTracker:
- المتعقب: يحمل معلومات حول محتوى الكائن المتعقب ومصدره:"auto">
- المحتوى: البيانات التي مرت من خلالها. على سبيل المثال، جميع البايتات التي مرت عبر
InputStream
أوOutputStream
. - المصدر: ربط نطاقات المحتوى بنطاقات المصدر الخاصة بها في أدوات التتبع الأخرى. على سبيل المثال، بالنسبة لبايتات ملف
String
يمكن أن يشير ذلك إلى نطاق جهاز التعقبFileInputStream
انString
تم القراءة من؛ مما يخبرنا من أي ملف وأين بالضبط في هذا الملف جاءت.
Map
الذي يربط الأشياء المثيرة للاهتمام بمتتبعها.byte
."-1" دير="auto">الأجهزة الأساسية
للحفاظ على تحديث أجهزة التتبع، تقوم أدواتنا بإدخال المكالمات إلى خطاف الأساليب في flowtracker عندما يتم استدعاء بعض أساليب JDK المحددة.
أبسط مثال على ذلك هو System.arraycopy. نعترض ذلك على جانب المتصل: المكالمات إلى java.lang.System.arraycopy
يتم استبدالها بالمكالمات إلى com.coekie.flowtracker.hook.SystemHook.arraycopy
بالنسبة لهذه الأجهزة والأجهزة الأخرى، نستخدم مكتبة معالجة البايت كود ASM. في SystemHook
نحن نسميها الحقيقي arraycopy
احصل على Trackers
من مصفوفات المصدر والوجهة من TrackerRepository
وتحديث الهدف Tracker
للإشارة إلى مصدره.
على سبيل المثال، بالنظر إلى هذا الكود:
char[] abc=...; char[] abcbc=new char[5];System.arraycopy(abc, 0, abcbc, 0, 3);System.arraycopy(abc, 1, abcbc, 3, 2);
يتم إعادة كتابة هذا على النحو التالي. لاحظ أن عملية التجهيز تتم على الكود الثنائي، وليس الكود المصدري، ولكننا نعرض هنا الكود المصدري المكافئ لأنه أسهل في القراءة.
char[] abc=...; char[] abcbc=new char[5];SystemHook.arraycopy(abc, 0, abcbc, 0, 3);SystemHook.arraycopy(abc, 1, abcbc, 3, 2);
بعد تنفيذ هذا، سيبدو المتعقب لـ abcbc على النحو التالي: {[0-2]: {tracker: abcTracker, sourceIndex: 0, length: 3}, [3-4]: {tracker: abcTracker, sourceIndex: 1, length: 2}}
كان هذا مثالاً على الخطاف من جانب المتصل. ولكن معظم المكالمات إلى خطاف تتم إضافة الأساليب على جانب المستدعى، داخل الأساليب في JDK. على سبيل المثال، خذ FileInputStream.read(byte[])
، الذي يقرأ البيانات من ملف ويخزن النتيجة في المجلد المقدم byte[]
نضيف المكالمة إلى طريقة الخطاف الخاصة بنا (FileInputStreamHook.afterReadByteArray
) في نهاية FileInputStream.read(byte[])
الطريقة. لدينا إطار عمل صغير خاص بنا للأدوات، مدفوعًا بالتعليقات التوضيحية، تم تنفيذه باستخدام ASM's AdviceAdapter
.
بهذه الطريقة نضيف خطافات إلى عدد من الفئات في JDK المسؤولة عن الإدخال والإخراج، مثل java.io.FileInputStream
, java.io.FileOutputStream
، والفئات الداخلية مثل sun.nio.ch.FileChannelImpl
, sun.nio.ch.IOUtil
, sun.nio.ch.NioSocketImpl
و اكثر.
التنفيذ:"https://github.com/coekie/flowtracker/blob/master/core/src/main/java/com/coekie/flowtracker/hook/SystemHook.java">نظام هوك,"https://github.com/coekie/flowtracker/blob/master/core/src/main/java/com/coekie/flowtracker/hook/FileInputStreamHook.java">ملفات إدخال التدفق هوك، وغيرها من الفئات في"https://github.com/coekie/flowtracker/tree/master/core/src/main/java/com/coekie/flowtracker/hook">حزمة الخطاف.
"-1" دير="auto">القيم البدائية، تحليل تدفق البيانات
التحدي الأكبر هو تتبع القيم البدائية. فكر في هذا المثال:
byte[] x; byte[] y;// ...byte b=x[1];// ...y[2]=b;
عند تنفيذ هذا الكود، سنحتاج إلى تحديث Tracker الخاص بـ y
، لتذكر أن القيمة عند الفهرس 2 تأتي من القيمة عند الفهرس 1 في x
. لو كان هؤلاء String[]
رمل b
كان String
بدلا من byte
، إذن لن نحتاج إلى تعديل الكود على هذا النحو، لأن TrackerRepository سيعرف ما هو Tracker للسلسلة، ويحتفظ بهذا الارتباط بغض النظر عن كيفية تمرير كائن السلسلة هذا. لكن TrackerRepository لا يمكنه الاحتفاظ بتعيين القيم الأولية مثل البايتات إلى Trackers، لأن القيم الأولية ليس لها هوية: أي Map
إن استخدام بايت كمفتاح من شأنه أن يخلط بين حالات مختلفة لنفس البايت. وبدلاً من ذلك، نقوم بتخزين ارتباط b
إلى متتبعه في متغير محلي في الطريقة نفسها. تتم إعادة كتابة الكود إلى شيء مثل هذا تقريبًا:
للقيام بذلك، يحتاج FlowTracker إلى فهم كيفية تدفق القيم عبر طريقة ما على وجه التحديد. نعتمد على دعم تحليل ASM لتحليل الكود (تفسير رمزيبهذه الطريقة، نقوم بإنشاء نموذج لمكان ظهور القيم في المتغيرات المحلية وعلى المكدس في كل نقطة في الطريقة، والمكان الذي تنتهي فيه.
تم تنفيذ ذلك في
- قيمة التدفق وفئاتها الفرعية (على سبيل المثال"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/ArrayLoadValue.java">قيمة تحميل المصفوفة) هذا النموذج الذي تأتي منه القيم، ويمكنه إنشاء التعليمات التي تنشئ نقاط التتبع التي تشير إلى هذا المصدر. ومن بين التعليمات المثيرة للاهتمام بشكل خاص"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/MergedValue.java">القيمة المدمجة، والذي يتعامل مع المواقف التي يمكن فيها أن تأتي القيمة من أماكن متعددة محتملة بسبب تدفق التحكم (على سبيل المثال عبارات if والحلقات).
- مترجم التدفق: تمديد ASM's
Interpreter
, يفسر تعليمات البايت كود، تنشئ الكود المناسبFlowValue
س. - المتجر وفئاتها الفرعية (على سبيل المثال"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/ArrayStore.java">مخزن المصفوفات) التي تمثل الأماكن التي تنتقل إليها FlowValues، والتي تستهلك TrackerPoints.
- محول التدفق:يقود عملية التحليل والقياس بأكملها. راجع وثائقه للحصول على شرح أكثر تفصيلاً لكيفية ترابط كل هذا معًا.
نحن لا نتتبع مصدر الجميع القيم البدائية. التركيز على byte
و char
القيم، وإلى حد أقل int
رمل long
س.
"-1" دير="auto">استدعاءات الطريقة
يقتصر تحليل تدفق البيانات من القسم السابق على التعامل مع تدفق القيم الأولية داخل طريقة واحدة. تتدفق هذه القيم أيضًا إلى طرق أخرى، كحجج وقيم إرجاع لاستدعاءات الطريقة. نقوم بنمذجة ذلك في Invocation
، الذي يخزن PointTracker
s للحجج وقيم الإرجاع. Invocation
يتم تخزينها في ThreadLocal
قبل استدعاء الطريقة مباشرةً، ويتم استرجاعها في بداية تنفيذ الطريقة.
على سبيل المثال، خذ هذا الكود الذي يمرر قيمة بدائية إلى "write" طريقة:
void caller() { byte b=...; out.write(b); }...class MyOutputStream { void write(byte value) { ... // do something with value }}
للحصول على TrackerPoint من b
الى داخل write
الطريقة، يتم إعداد الكود على النحو التالي:
فارغ المتصل() { بايت ب=...; نقطة التتبع بتراكر=...; الإستدعاء.يخلق("write(byte)") .مجموعة Arg(0, بتراكر) // هذا يضع الاستدعاء في ThreadLocal .نداء(); خارج.يكتب(ب); } ...فصل تيار الإخراج الخاص بي { فارغ يكتب(بايت قيمة) { // هذا يستخرج الاستدعاء من ThreadLocal الإستدعاء الإستدعاء=الإستدعاء.يبدأ("write(byte)"); نقطة التتبع متتبع القيمة=الإستدعاء.الحصول على Arg0(); ... // افعل شيئًا باستخدام value وvalueTracker } }
التنفيذ:"https://github.com/coekie/flowtracker/blob/master/core/src/main/java/com/coekie/flowtracker/tracker/Invocation.java">الاستدعاء,"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/InvocationArgStore.java">متجر InvocationArg,"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/InvocationArgValue.java">قيمة InvocationArg,"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/InvocationReturnStore.java">استدعاء العودة إلى المتجر,"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/InvocationReturnValue.java">قيمة إرجاع الاستدعاء,"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/InvocationOutgoingTransformation.java">استدعاءالتحويل الصادر,"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/InvocationIncomingTransformation.java">استدعاءالتحويل الوارد
هناك نوعان رئيسيان من مصادر البيانات التي يتم تتبعها. هناك الإدخال/الإخراج، والذي يتم تتبعه كما هو موضح في "Basic instrumentation" القسم. وهناك قيم تأتي من الكود نفسه، مثل الثوابت البدائية والسلاسل ('a'
, "abc"
). بالنسبة لهؤلاء، نقوم بإنشاء متتبع لكل فئة (أ ClassOriginTracker
)، الذي يحتوي على تمثيل نصي لتلك الفئة والثوابت التي تشير إليها. عندما تتم الإشارة إلى تلك الثوابت، فإننا نشير بعد ذلك إلى المتتبعات لتلك القيم في المكان المقابل في هذا التمثيل النصي. وهذا هو كأنه إن التمثيل النصي للفئة هو المكان الذي تمت قراءة القيم منه. وهذا يجعل نموذجنا للثوابت يبدو مشابهًا جدًا لكيفية نمذجة الإدخال/الإخراج.
على سبيل المثال لهذا الكود:
class MyClass { void myMethod() { char a='x'; ... // do something with a }}
نحن نولد ClassOriginTracker
مع المحتوى الذي يبدو مثل هذا:
class MyClassvoid myMethod(): (line 3): x
ويتم إعادة كتابة الكود إلى شيء مثل:
class MyClass { void myMethod() { char a='x'; TrackerPoint aTracker=ConstantHook.constantPoint( 1234 /* id for MyClass*/, 81 /* offset of 'x' in the ClassOriginTracker content */); ... // do something with a and aTracker }}
لأسباب تتعلق بالأداء، نستخدم في الواقع ConstantDynamic ("https://openjdk.org/jeps/309" rel="nofollow">جيب 309) للتأكد من أن constantPoint
يتم استدعاء الأساليب مرة واحدة فقط بدلاً من كل مرة myMethod
ينفذ.
التنفيذ:"https://github.com/coekie/flowtracker/blob/master/core/src/main/java/com/coekie/flowtracker/tracker/ClassOriginTracker.java">متتبع أصل الفئة,"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/ConstantValue.java">القيمة الثابتة,"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/ConstantsTransformation.java">التحويل الثابت
"-1" دير="auto">السلاسل الحرفية
بالنسبة للسلاسل الحرفية، نقوم بإنشاء نسخة جديدة من السلسلة، وربط محتوى السلسلة (السلسلة) بـ byte[]
في String.value
) مع ClassOriginTracker
. بيان مثل السلسلة س="abc"؛
يتم إعادة كتابته إلى السلسلة s=StringHook.constantString("abc"، 1234، 81)؛
يؤدي هذا إلى كسر الضمان الذي توفره JVM عادةً، وهو أن جميع ثوابت السلسلة معتقل:يجب أن تشير جميع حالات حدوث نفس ثابت السلسلة إلى نفس المثيل. لا تعتمد معظم التعليمات البرمجية في الواقع على تدريب السلسلة، ولكن التعليمات البرمجية التي تعتمد على ذلك ستتعطل بواسطة أدواتنا. نتجنب معظم المشكلات التي قد تسببها لأن:
- نحن نستخدم ConstantDynamic، وبالتالي فإن نفس السلسلة الحرفية (في نفس سطر التعليمات البرمجية) التي يتم تنفيذها عدة مرات لا تزال تعطي نفس المثيل في كل مرة.
- نحن نعيد كتابة بعض
stringA==stringB
تعبيرات مثلObjects.equals(stringA, stringB)
، بحيث يبدو من بعض وجهات النظر أنهم يشبهون نفس المثيل مرة أخرى. - نقوم بتعطيل تتبع الأحرف النصية في بعض الحزم (مثل
java.lang.*
). هذا قابل للتكوين (انظرbreakStringInterning
في"http://github.com/coekie/flowtracker/blob/master/USAGE.md">الاستخدام.md).
التنفيذ:"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/StringLdc.java">سلسلةLdc,"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/ConstantsTransformation.java">التحويل الثابتمقارنة السلاسل
"-1" دير="auto">الرجوع إلى القيم غير المتعقبة
لا يتتبع FlowTracker كل قيمة في البرنامج. ويرجع ذلك جزئيًا إلى مخاوف تتعلق بالأداء، وجزئيًا لأننا لم ننفذ كل ما نريده، وجزئيًا لأنه لا يبدو ذا صلة أو يتطلب بناء نموذج بيانات أكثر تعقيدًا حيث يمكن أن تأتي القيم من مجموعة من الأماكن (على سبيل المثال القيم العددية المحسوبة). عندما تنتهي القيم التي لا يتم تتبعها في الأماكن التي نريد أن نبدأ في تتبعها، فإننا نعاملها على غرار الثوابت: نضيف رابطًا إلى ClassOriginTracker
، إلى حيث تم تعقبهم، وتم تمثيلهم هناك على أنهم ">"
على سبيل المثال، أطوال المصفوفات هي قيم لا يتم تتبعها، لذا افترض أن إحدى الطرق تستدعي write(array.length)
، ثم في ذلك Invocation
نحن نمرر PointTracker
وهذا يشير إلى ذلك المكان في الكود حيث write
يتم استدعاء الطريقة.
في الممارسة العملية، تكون النتيجة هي عندما تنظر إلى بعض المخرجات، وخاصةً إذا كانت بتنسيق ثنائي، بينما لا ترى من أين جاءت القيمة في الأصل، فلا يزال بإمكانك غالبًا فك رموز ما تعنيه بسرعة (على سبيل المثال "that value just before that tracked String points to write(array.length)
, so that must be the length of that String").
هناك المزيد من الموضوعات حول التنفيذ والتي يمكن التحدث عنها، ولكنها لم تصل إلى المستوى المطلوب. تم توثيق معظم ذلك في الكود، إذا كنت تريد حقًا معرفة المزيد:
- تفاصيل عن"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/MergedValue.java">القيمة المدمجة:الجزء الأصعب في تحليل تدفق البيانات هو كيفية استخدام التعليمات البرمجية لتتبع القيم من خلال الفروع والحلقات.
- كيف نربط سلسلة السلاسل من خلال تعريفها ("https://openjdk.org/jeps/280" rel="nofollow">جيب 280) عن طريق إضافة خطافات إلى MethodHandles التي تم إرجاعها بواسطة StringConcatFactory في"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/StringConcatenation.java">تسلسل السلاسل و"https://github.com/coekie/flowtracker/blob/master/core/src/main/java/com/coekie/flowtracker/hook/StringConcatFactoryHook.java">StringConcatFactoryHook.
- العثور على الكود المصدر، وفك التجميع باستخدام Vineflower، وربط الكود الثنائي بسطور الكود المصدر. انظر"https://github.com/coekie/flowtracker/blob/master/web/src/main/java/com/coekie/flowtracker/web/SourceCodeGenerator.java">مولد الكود المصدر,"https://github.com/coekie/flowtracker/blob/master/web/src/main/java/com/coekie/flowtracker/web/VineflowerCodeGenerator.java">مولد كود Vineflower,"https://github.com/coekie/flowtracker/blob/master/web/src/main/java/com/coekie/flowtracker/web/AsmCodeGenerator.java">مولد الكود Asm.
- إعداد ClassLoader. كيف نتجنب التبعيات على bootclasspath التي تتعارض مع التطبيق، بدون تظليل (لأن ذلك يجعل التصحيح مزعجًا) وبدون ملفات jar المتداخلة. إعداد التطوير الذي يسمح بتغيير وكيل دون إعادة تعبئته، لضمان دورات تطوير سريعة. انظر"https://github.com/coekie/flowtracker/blob/master/agent/src/main/java/com/coekie/flowtracker/agent/FlowTrackerAgent.java">عامل تعقب التدفق,"https://github.com/coekie/flowtracker/blob/master/agent/src/main/java/com/coekie/flowtracker/agent/DevAgent.java">ديفاجينت,"https://github.com/coekie/flowtracker/blob/master/agent/src/main/java/com/coekie/flowtracker/agent/SpiderClassLoader.java">محمل فئة العنكبوت.
- كيف يمكن أن يتدخل تحميل الفصل في تتبع استدعاءات الطريقة، وكيف نعمل على تجاوز ذلك. راجع"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/SuspendInvocationTransformer.java">تعليق محول الاستدعاء,"https://github.com/coekie/flowtracker/blob/72ab61da96cbbb236a7395f9226f1797fa851892/core/src/main/java/com/coekie/flowtracker/tracker/Invocation.java#L163-L189">استدعاء#تعليق.مشكلة مثيرة للاهتمام، والحل بسيط وواضح إلى حد ما في الماضي.
- تتبع القيم البدائية المخزنة في الحقول:"https://github.com/coekie/flowtracker/blob/master/core/src/main/java/com/coekie/flowtracker/tracker/FieldRepository.java">مستودع الحقل,"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/FieldStore.java">المتجر الميداني,"https://github.com/coekie/flowtracker/blob/master/weaver/src/main/java/com/coekie/flowtracker/weaver/flow/FieldValue.java">قيمة الحقل.المزيد من نفس الشيء، لا شيء مفاجئ.
- كيف نضيف التعليقات إلى الكود المبرمج للمساعدة في فهم وتصحيح أخطاء الأجهزة. لا يدعم ASM/Bytecode التعليقات، ولكن هذا لن يوقفني!
- تجنب مشاكل الدائرية عند تجهيز فئات JDK الأساسية. انا اكل
ClassCircularityError
رملStackOverFlowError
س لتناول الافطار. - الواجهة الأمامية: خادم ويب مع jetty وJAX-RS. واجهة مستخدم ويب مبنية باستخدام Sv إلتي. تصميم واجهة مستخدم جميل بواسطة... لا أحد.
- رجس ThreadLocal المحسن لدينا في
ContextSupplier
. بعد تفكير ثانٍ، لا يهم، فأنت لا تريد أن تعرف.