Heisenbug — ایک بگ جو ڈیبگ کرنے کی کوشش کرنے پر غائب ہو جاتا ہے۔ یہ اصطلاح ہائیزنبرگ کے غیر یقینی اصول سے ماخوذ ہے: مشاہدہ نظام کے رویے کو متاثر کرتا ہے۔ موبائل ڈویلپمنٹ میں، Heisenbug سب سے مشکل مسائل میں سے ایک ہے کیونکہ ڈیبگنگ کے معیاری طریقے (لاگز، بریک پوائنٹس، اضافی کوڈ) پروگرام کی حالت بدل دیتے ہیں اور بگ چھپا دیتے ہیں۔ آئیے اسباب اور مشکل سے پکڑے جانے والی غلطیوں سے نمٹنے کے طریقے سمجھتے ہیں۔
اہم نکات
Heisenbug — غلطیوں کا ایک طبقہ جو پروڈکشن یا عام آپریشن میں ظاہر ہوتا ہے، لیکن ڈیبگنگ ماحول میں دوبارہ پیدا کرنے کی کوشش کرنے پر غائب ہو جاتا ہے۔ یہ اصطلاح 1980 کی دہائی میں پروگرامر Jim Gray نے تقسیم شدہ نظاموں کے سیاق و سباق میں وضع کی تھی، لیکن آج یہ موبائل ایپلیکیشنز کے لیے ان کی غیر متزامن نوعیت کی وجہ سے سب سے زیادہ متعلقہ ہے۔
بنیادی وجہ: معیاری ڈیبگنگ ٹولز عملدرآمد کے ماحول کو بدل دیتے ہیں۔ ایک بریک پوائنٹ تھریڈ کو کئی ملی سیکنڈ کے لیے روکتا ہے، لاگنگ ہم وقت ساز I/O شامل کرتی ہے، اضافی جانچ آپریشنز کی ترتیب بدل دیتی ہے۔ ملٹی تھریڈ ماحول میں، مائیکرو سیکنڈ کی تاخیر بھی تھریڈ عملدرآمد کی ترتیب بدل سکتی ہے اور ڈیٹا ریس چھپا سکتی ہے۔
Microsoft Research (2022) کے مطابق، ملٹی تھریڈ موبائل ایپلیکیشنز میں تقریباً 15-25% بگز Heisenbug کے طور پر درجہ بند کیے جاتے ہیں۔ ساتھ ہی، ایک Heisenbug کو تلاش کرنے اور ٹھیک کرنے میں لگنے والا وقت عام بگ سے اوسطاً 5-10 گنا زیادہ ہوتا ہے، کیونکہ اسے براہ راست دوبارہ پیدا نہیں کیا جا سکتا۔
فہرست میں تیزی سے سوائپ کرنے پر ایپ پروڈکشن میں کریش ہو جاتی ہے، لیکن ڈیبگر سے منسلک ہونے یا لاگز شامل کرنے پر — یہ بالکل کام کرتی ہے۔ وجہ: UI تھریڈ (RecyclerView اپ ڈیٹ) اور بیک گراؤنڈ تھریڈ (اڈاپٹر ڈیٹا اپ ڈیٹ) کے درمیان ڈیٹا ریس۔ لاگز ایک تاخیر شامل کرتے ہیں جو تھریڈز کو تصادفی طور پر ہم آہنگ کرتی ہے۔
Bohrbug — ایک پیش قیاسی، مستحکم طور پر دوبارہ پیدا ہونے والا بگ۔ بوہر کے ایٹمی ماڈل سے تشبیہ دے کر نامزد کیا گیا: ایٹم کی طرح، بگ ہر بار مشاہدہ کرنے پر یکساں رویہ دکھاتا ہے۔ مثال: ڈیٹا لوڈ ہونے سے پہلے بٹن دبانے پر NullPointerException۔ معیاری یونٹ ٹیسٹنگ سے علاج۔
Mandelbug — پیچیدہ، افراتفری والے وجہ اور اثر کے تعلقات والا بگ (مینڈل بروٹ سیٹ سے تشبیہ دے کر نامزد کیا گیا)۔ صرف حالات کے ایک مخصوص امتزاج کے تحت ظاہر ہوتا ہے: OS ورژن، ڈیوائس ماڈل، نیٹ ورک کی حالت۔ یہ Heisenbug سے اس لحاظ سے مختلف ہے کہ یہ ڈیبگنگ کے دوران غائب نہیں ہوتا — مسئلہ دوبارہ پیدا کرنے کی مشکل ہے، اوزاروں سے رویہ تبدیل ہونا نہیں۔
Heisenbug — ایک بگ جو خاص طور پر ڈیبگنگ ٹولز کی وجہ سے غائب ہو جاتا ہے۔ اگر آپ لاگ شامل کرتے ہیں — بگ غائب ہو جاتا ہے۔ اگر آپ بریک پوائنٹ لگاتے ہیں — بگ ظاہر نہیں ہوتا۔ اگر آپ سب کچھ ہٹا دیتے ہیں — بگ واپس آ جاتا ہے۔ بنیادی وجہ: ڈیبگنگ کے دوران تبدیل شدہ ٹائمنگ۔
| قسم | دوبارہ پیدا کرنے کی صلاحیت | ڈیبگنگ پر ردعمل | مثال |
|---|---|---|---|
| Bohrbug | 100% | بدستور | خالی فہرست پر NPE |
| Mandelbug | افراتفری | بدستور | Android 12، Samsung، کم بیٹری پر کریش |
| Heisenbug | صرف ڈیبگنگ کے بغیر | غائب | لاگز سے غائب ہونے والی ڈیٹا ریس |
| Schrödinbug | کوڈ میں ظاہر نہیں ہوتا | دیکھنے پر ظاہر | بگ کوڈ میں نظر آتا ہے لیکن کبھی متحرک نہیں ہوتا |
Race condition — Heisenbug کی پہلی وجہ۔ دو تھریڈ ہم آہنگی کے بغیر مشترکہ ڈیٹا تک رسائی حاصل کرتے ہیں۔ ڈیبگر ایک تاخیر متعارف کراتا ہے، جس سے تھریڈ قدرتی طور پر ہم آہنگ ہو جاتے ہیں۔ ڈیبگر کے بغیر، عملدرآمد کی ترتیب غیر متوقع ہے۔
ٹائمنگ پر منحصر غلطیاں — بگز جو صرف ایک مخصوص عملدرآمد کی رفتار پر ظاہر ہوتے ہیں۔ مثال: ایک اینیمیشن جو اگلے آپریشن کے شروع ہونے سے پہلے ختم ہونی چاہیے۔ ڈیبگر میں، اینیمیشن سست چلتی ہے اور آپریشن اینیمیشن ختم ہونے کے بعد شروع ہونے کا وقت پا لیتا ہے۔ پروڈکشن میں — اس کے برعکس۔
// Example race condition — typical Heisenbug
class ListViewModel : ViewModel() {
private var items = mutableListOf<String>()
fun loadFromNetwork() {
viewModelScope.launch(Dispatchers.IO) {
val result = api.fetchItems()
items.addAll(result) // ❌ Not thread-safe
}
}
fun getItems(): List<String> = items.toList()
// Race condition: getItems read may overlap with loadFromNetwork write
}
کمپائلر آپٹیمائزیشن — کمپائلر (JIT، ART، Kotlin/Native) آپٹیمائزیشن کے لیے ہدایات کو دوبارہ ترتیب دے سکتا ہے۔ ڈیبگ بلڈ میں، آپٹیمائزیشن غیر فعال ہوتی ہیں اور کوڈ «جیسا لکھا گیا ہے» ویسا ہی عملدرآمد ہوتا ہے۔ ریلیز بلڈ میں، کمپائلر آپریشنز کی ترتیب بدل دیتا ہے، جو کوڈ میں چھپی ہوئی مفروضوں کو ظاہر کر سکتا ہے۔
ThreadSanitizer (TSan) — C/C++ اور Kotlin/Native میں ڈیٹا ریس کا پتہ لگانے کا گوگل کا آلہ۔ یہ بلڈ میں شامل ہوتا ہے اور ہم آہنگی کے بغیر کسی بھی مشترکہ میموری تک رسائی کا پتہ لگاتا ہے۔ لاگز کے برعکس، TSan ٹائمنگ کو متاثر نہیں کرتا کیونکہ یہ I/O کے بجائے انسٹرومینٹڈ کوڈ کے ذریعے کام کرتا ہے۔
تعینی ٹیسٹ — حقیقی غیر متزامنیت کو کنٹرول شدہ غیر متزامنیت سے بدلیں۔ عملدرآمد کی ترتیب پر مکمل کنٹرول کے لیے TestDispatcher (Kotlin)، RxJava Plugins یا GCD ٹیسٹ قطاریں (iOS) استعمال کریں۔ مخصوص منظرنامے طے کریں: تھریڈ A چلتا ہے، پھر B، پھر دوبارہ A۔
چکریہ لاگنگ — میموری میں رنگ بفر میں لاگنگ (ڈسک پر نہیں)۔ جب بگ ہوتا ہے، بفر فائل میں محفوظ کیا جاتا ہے۔ چونکہ میموری میں لکھنے میں نینو سیکنڈ لگتے ہیں (ڈسک I/O کے لیے ملی سیکنڈ کے بجائے)، ایسی لاگنگ ٹائمنگ کو متاثر نہیں کرتی اور Heisenbug کو نہیں چھپاتی۔
class CyclicBuffer(val capacity: Int = 1000) {
private val buffer = ArrayDeque<String>(capacity)
private val lock = Any()
fun log(message: String) {
synchronized(lock) {
if (buffer.size >= capacity) buffer.removeFirst()
buffer.addLast(message)
}
}
fun flush() {
synchronized(lock) { buffer.forEach { fileWriter.write(it) } }
}
}
پروڈکشن میں لاگنگ — اگر بگ مقامی طور پر دوبارہ پیدا نہیں ہوتا، تو پروڈکشن میں ڈیٹا جمع کریں۔ Firebase Crashlytics لاگز، Sentry Breadcrumbs یا کسٹم چکریہ لاگر استعمال کریں۔ اہم: لاگنگ غیر متزامن ہونی چاہیے اور کارکردگی پر کم سے کم اثر ڈالنی چاہیے۔
حالت کی علیحدگی — مشترکہ قابل تبدیل حالت کو کم سے کم کریں۔ ہر جزو کی اپنی الگ تھلگ حالت ہونی چاہیے، جو دوسرے اجزاء سے براہ راست لکھنے کے لیے ناقابل رسائی ہو۔ Unidirectional Data Flow (UDF) استعمال کریں — حالت ایک سمت میں بہتی ہے: واقعہ → کم کرنے والا → حالت → UI۔
فعالی طریقہ — ضمنی اثرات کے بغیر خالص افعال کی جانچ اور ڈیبگ کرنا آسان ہے۔ ضمنی اثرات (نیٹ ورک، DB، فائلیں) سختی سے متعین تہوں (ریپوزٹری، ڈیٹا سورس) میں الگ کریں۔ فعالی کوڈ میں تھریڈنگ کی غلطیاں عملی طور پر ناممکن ہیں۔
سخت موڈ — ڈیبگ بلڈ میں Android StrictMode فعال کریں۔ یہ تھریڈنگ پالیسی کی خلاف ورزیاں (مین تھریڈ پر نیٹ ورک، مین تھریڈ پر ڈسک I/O) کا پتہ لگاتا ہے اور ایک استثنا پھینکتا ہے۔ یہ ممکنہ Heisenbug کو ایک تعینی Bohrbug میں بدل دیتا ہے جو فوری طور پر نظر آتا ہے۔
class DebugApplication : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build()
)
}
}
}
غیر متزامنیت پر توجہ کے ساتھ کوڈ کا جائزہ — عمل کا ایک لازمی حصہ۔ ہر پل ریکویسٹ کو مشترکہ قابل تبدیل حالت، غیر تھریڈ سیف کلیکشنز اور ہم آہنگی کی کمی کے لیے چیک کیا جانا چاہیے۔ مخصوص پیٹرن کو خودکار طور پر منع کرنے کے لیے lint اصول استعمال کریں (مثال: synchronized کے بغیر MutableList تک رسائی)۔
اکثر پوچھے گئے سوالات
کیونکہ معیاری طریقے — بریک پوائنٹس، لاگز، print — عملدرآمد کے ماحول کو اتنا بدل دیتے ہیں کہ بگ ظاہر ہونا بند ہو جاتا ہے۔ ڈیبگر تمام تھریڈز کو دسیوں ملی سیکنڈ کے لیے روکتا ہے۔ اس دوران، ڈیٹا ریس جو بگ پیدا کر رہی تھی، قدرتی طور پر حل ہو جاتی ہے۔ ایسے اوزار درکار ہیں جو عملدرآمد کی ٹائمنگ کو متاثر نہ کریں۔
Mandelbug حالات کی پیچیدگی کی وجہ سے دوبارہ پیدا کرنا مشکل ہے، لیکن ڈیبگنگ ٹولز اس کے ظہور کو متاثر نہیں کرتے۔ Heisenbug خاص طور پر ڈیبگنگ ٹولز کی وجہ سے غائب ہو جاتا ہے۔ Mandelbug کی مثال: صرف Android 11، 3 GB RAM اور 15% سے کم بیٹری والے آلات پر کریش۔ Heisenbug کی مثال: Log.d() شامل کرنے پر غائب ہونے والی ڈیٹا ریس۔
Flaky test detection استعمال کریں — ٹیسٹ جو کبھی ناکام ہوتے ہیں، کبھی کامیاب۔ Android میں، ٹیسٹ تنہائی کے لیے Android Test Orchestrator استعمال کریں۔ ڈیبگ ٹیسٹ میں StrictMode شامل کریں۔ بلڈ کو ThreadSanitizer سے انسٹرومینٹ کریں۔ اگر ٹیسٹ >5% رنز میں flaky ہے — اسے ممکنہ Heisenbug سمجھیں اور انضمام سے پہلے تحقیق کریں۔
جزوی طور پر۔ Kotlin میں Flow اور ساختہ ہم آہنگی مشترکہ قابل تبدیل حالت کی مقدار کم کرتے ہیں اور تھریڈ مینجمنٹ آسان بناتے ہیں۔ لیکن coroutines تھریڈ سیفٹی کی ضمانت نہیں دیتے: اگر دو coroutines حالت بانٹتے ہیں، تو ڈیٹا ریس اب بھی ممکن ہے۔ مشترکہ حالت کے تحفظ کے لیے Mutex یا coroutines کے درمیان ڈیٹا منتقل کرنے کے لیے Channel استعمال کریں۔
میموری میں چکریہ لاگ بفر استعمال کریں جو غلطی پر خودکار طور پر فلش ہو۔ کسٹم breadcrumbs کے ساتھ Crashlytics یا Sentry کے ذریعے تفصیلی نگرانی شامل کریں۔ Android کے لیے، ANR کا پتہ لگانا فعال کریں اور traces دیکھیں۔ اگر بگ ڈیٹا ریس ہے، تو پروڈکشن جیسے لوڈ کے ساتھ ڈیبگ بلڈ میں ThreadSanitizer مسئلہ ظاہر کر سکتا ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں