باگ چيست؟
همان طور كه مي‌دانيد، به هر نقص يا ايراد يك نرم‌افزار يا برنامه كامپيوتري باگ مي‌گويند. باگ از نظر لغوي يعني حشره كوچك و در تاريخ مهندسي نرم‌افزار گفته مي‌شود اين اصطلاح را اولين بار Grace Hoper، خانمي كه در دانشگاه هاروارد مشغول تحصيل و تحقيق در رشته كامپيوتر بود، به كار برده است. او كه در حال كار با كامپيوترهايMark II و Mark III بود، يك‌بار با مشكل مواجه شد و تكنيسين‌هايي كه براي بررسي مشكل و تعمير كامپيوتر، آن را باز كرده بودند سوسكي را پيدا كردند كه وارد دستگاه شده بود و آن را از كار انداخته بود.

البته در حقيقت اين واژه را اولين بار همان تكنيسين‌هايي كه اين حشره را داخل دستگاه يافته بودند، طي يادداشتي (اولين دليل واقعي باگ / ايراد برنامه پيدا شد) به شوخي به كاربرده بودند. البته اين تكنيسين‌ها يا خانم هوپر اولين كساني نبودند كه از اين واژه براي اشاره به يك ايراد در دستگاهي استفاده مي‌كردند. آن‌ها صرفاً براي نخستين بار از اين اصطلاح در دنياي كامپيوتر استفاده كردند، ولي اعتقاد بر اين است كه اصطلاح Debugg توسط همين افراد ابداع شد.

موضوع باگ يكي از سرفصل‌هاي مهم رشته مهندسي نرم‌افزار است. از اين رو متون و كتاب‌هاي مفصلي در زمينه Debugging يا اشكال‌زدايي از نرم‌افزار و متدهاي آن تأليف شده است و همچنان ادامه دارد. برنامه‌نويسان تازه‌كار معمولاً از اين شاخه مهندسي نرم‌افزار گريزانند و اميدوارند برنامه‌هايي بنويسند كه به قدري خوب باشد كه اصلاً كارش به اشكال زدايي نكشد، ولي پس از دو سه سال كار حرفه‌اي در اين زمينه سرانجام تسليم‌ مي‌شوند و آشنايي با اصول علمي اشكال زدايي برايشان به يك ضرورت تبديل مي‌شود؛ مگر اين‌كه نخواهد به اصول اخلاقي و حرفه‌اي مهندسي نرم‌افزار متعهد باشند و از اين‌كه برنامه‌هاي ساخت آن‌ها پر از انواع باگ و ايراد باشد، باكي نداشته باشند، اما برطرف كردن باگ‌ها براي بسياري از برنامه‌نويسان غيرآماتور يكي از قسمت‌هاي چالش برانگيز و لذت بخش كار است و تقريباً مثل حل كردن معما است.

برنامه‌نويساني كه دائماً به فكر كاستن از باگ‌ها و ايرادهاي نرم‌افزارهاي خود هستند، در حقيقت به طور مداوم در حال انجام يك ورزش فكري هستند كه رشته‌اي تو در تو از حلقه‌هاي پرسش و پاسخ را در دل خود دارد. با اين همه،‌ اجازه بدهيد به اختصار ببينيم اصولاً چرا باگ‌ها به وجود مي‌آيند و ريشه اين معضل كجاست؟

چرا باگ‌ها پديد مي‌آيند؟
وقتي در دنياي سيستم‌هاي ديجيتالي و كامپيوتري از باگ صحبت مي‌كنيم، مقصودمان بيشتر يك نقص نرم‌افزاري است و كمتر پيش مي‌آيد يك نقص ديجيتالي سخت‌افزاري را باگ بناميم. هرچند كه اين لغت از نظر تاريخي در مهندسي مكانيك و ادوات سخت‌افزاري ريشه دارد. بنابراين آن دسته از وسايل ديجيتالي كه فاقد نرم‌افزارند، اصولاً در اين بحث جاي نمي‌گيرند. مثلاً يك دستگاه ويديو را با يك كامپيوتر مقايسه كنيد. هر وقت كه مي‌خواهيد ويديو را روشن كنيد، يا روشن مي‌شود يا نمي‌شود و حالت ديگري در اين ميان وجود ندارد، اما وقتي يك كامپيوتر را روشن مي‌كنيد، ممكن است سيستم عامل بالا نيايد (بوت نشود). در اين صورت مي‌توان گفت كامپيوتر واقعاً به كار نيفتاده است؛ زيرا با اين كه روشن است، كار نمي‌كند. بنابراين، هر مشكلي هست، در ماهيت نرم‌افزاري باگ نهفته است.

در اينجا به پرسش اساسي‌تري مي‌رسيم: اصلاً نرم‌افزار چيست؟ اين پرسش كوچكي نيست و من هم در اين يادداشت كوچك ادعاي پاسخ دادن به آن را ندارم، اما تا آنجا كه به بحث ما مربوط مي‌شود، مي‌توان گفت نرم‌افزار بسته‌اي حاوي يك يا چند دستورالعمل است كه كسي (پردازنده) آن را در جايي (حافظه) اجرا مي‌كند. پردازنده‌هاي امروزي به ندرت اشتباه مي‌كنند. به علاوه، خود آن يك سخت‌افزار ديجيتالي است كه بحث باگ چندان درباره عملكرد آن صدق نمي‌كند. در واقع اگر انصاف بدهيد، احتمال اشتباه محاسباتي پردازنده‌هاي امروزي اينتل و AMD در كامپيوترهاي ما چنان ناچيز است كه اصلاً قابل مطرح كردن نيست. پس بايد بدانيم كه اين دستورالعمل كجا اجرا مي‌شود؟ جواب <حافظه> است. در نتيجه پاسخ اين معما هرچه باشد، ماجرايي است كه در ارتباط با دستورالعمل‌ها و حافظه رخ مي‌دهد.

زنجيره عمليات‌
ساده‌ترين باگ نرم‌افزاري وقتي به وجود ميآيد كه يك دستورالعمل غلط باشد يا از منطق نارسايي پيروي كند. مثلاً اغلب اوقات فرايند‌ها به صورت مركب اجرا مي‌شوند: ابتدا كاربر عمل A را انجام مي‌دهد، سپس نرم‌افزار روتين B را اجرا مي‌كند و آن‌گاه فرايند C اجرا مي‌شود، ولي فكرش را نكرده بوديد اگر دفعتاً ابتدا B اجرا شود و سپس كاربر عمل A را انجام دهد و آن‌گاه C به اجرا گذاشته شود، چه اتفاقي خواهد افتاد؟ اين ممكن است به سادگي منجر به پيدايش باگ ‌شود. افزودن قابليت‌هاي جديد به نرم‌افزار نيز گاهي باگ‌هاي تازه‌اي را پديد مي‌آورد. اين باگ‌ها اغلب ناشي از ظاهر شدن فرايندهاي جديدي هستند كه زنجيره عمليات يك دستورالعمل مركب را پيچيده‌تر، و توالي‌هاي غيرمجاز آن‌ها را بيشتر مي‌كنند، اما اين تنها يك بعد قضيه است.

شرايط مرزي‌
يك ركن اساسي در پيدايش باگ‌ها موضوعي است كه شرايط مرزي (Boundary Conditions) مي‌ناميم. آيا تا به حال از خودتان پرسيده‌ايد اگر يك روز با بالاترين سرعتي كه سرعت‌سنج اتومبيل شما نشان مي‌دهد برانيد، چه اتفاقي ممكن است بيفتد؟ شايد موتور ماشين منفجر شود! به اين وضعيت خاص كه بخشي از يك سيستم با شديدترين ورودي‌ها (در اينجا پدال گاز و دنده خودرو) و شديدترين خروجي‌ها (سرعت خودرو) روبه‌رو مي‌شود، اصطلاحاً <شرايط مرزي> يك سيستم مي‌گويند.

نرم افزارها نيز سيستم هستند و به دليل ماهيت خود، به آساني ممكن است در معرض شرايط مرزي قرار بگيرند و در چنان شرايطي رفتار نامتعادل و پيش‌بيني نشده‌اي از خود نشان دهند. مثلاً امتحان كنيد ببينيد اگر به جاي تايپ كردن يك عدد معمولي در قسمتي از يك نرم‌افزار حسابداري، حداكثر ارقام ممكنه (مثلا‌ً 9 رقم) را در آن تايپ كنيد و كاري كنيد كه در خودش ضرب شود، چه اتفاقي مي‌افتد؟ آيا سرريز مي‌كند؟ اگر نرم‌افزار قاطي كرد، اين يك باگ است. دادن ورودي‌هاي غيرطبيعي به سيستم نيز ممكن است همين پيامدها را داشته باشد. بعيد است هيچ انسان عاقلي در باك بنزين ماشينش چند ليتر مايع ظرف شويي بريزد، اما در قسمتي از يك نرم‌افزار حسابداري مي‌توان به جاي تايپ يك عدد، يك مگابايت Text به صورت copy-paste وارد كرد تا ببينيم آيا نرم‌افزار قاطي مي‌كند يا خير و اين كاري است كه هكرها خيلي به آن علاقه دارند!

حافظه؛ شاه كليد باگ‌
اكنون اين پرسش را مي‌توان مطرح كرد كه ماهيت شرايط مرزي در نرم‌افزارها چيست و چه عواملي اين شرايط مرزي را تعيين مي‌كنند؟ خوب كه دقت كنيد، متوجه مي‌شويد شرايط مرزي (نوع متغيرها و حجم ورودي قابل‌پذيرش توسط آن‌ها) موضوعي است كه به حافظه مربوط مي‌‌شود. در شرايط خيالي، حافظه مورد استفاده نرم‌افزار شما بي‌نهايت است، ولي در شرايط واقعي نرم‌افزارتان مرتباً به ديواري به نام <محدوديت‌هاي حافظه> برخورد مي‌كند. بنابراين چه هنگامي كه با مسئله شرايط مرزي روبه‌رو هستيد و چه هنگامي كه پردازنده كامپيوتر دستورالعمل نارساي شما را اجرا مي‌كند، منشأ پيدايش باگ‌ها، اتفاقات پيش‌بيني نشده‌اي است كه درون حافظه رخ مي‌دهند. آنجا محل زد و خورد داده‌هاي قد و نيم‌قدي است كه گاه بسيار كوچك‌تر از شرايط مرزي حافظه هستند و گاه با ديواره‌هاي آن برخورد مي‌كنند. گاهي كوچكند، اما سرشماري آن‌ها مثل شمارش كودكان در حال بازي در حياط يك دبستان شلوغ، كار سختي است.

دستورالعمل‌ها و متغيرها مي‌آيند و مي‌روند. بعضي پاك مي‌شوند و بعضي همچنان در گوشه‌اي از حافظه مي‌مانند و بايت‌هايي كه مي‌مانند، شرايط مرزي را براي دستورالعمل‌ها و ورودي‌هاي بعدي دشوارتر مي‌كنند. بامزه‌ترين نوع باگ‌ها در فرايندهاي مركب پديد مي‌آيند. مثلاً روتين A را به حافظه مي‌فرستيد تا آنجا دنبال مقداري بگردد؛ غافل از اين‌كه يك روتين ديگر قبلاً به صورت اتفاقي آنجا آمده و آن مقدار را پاك كرده است و حالا روتين A سرگردان اين سو و آن سوي حافظه دنبال گمشده‌اش مي‌گردد!

نتيجه گيري اخلاقي!
يك ديدگاه اين است كه علت اصلي به وجود آمدن باگ‌ها فضايي است كه حافظه در اختيار دستورالعمل‌هاي برنامه ما مي‌گذارد. از آنجايي كه دقيقاً نمي‌دانيم اين فضا براي برنامه ما كافي است يا خير يا نمي‌توانيم تمام توالي‌هاي ممكن در فرايندهاي مركب را پيش‌بيني كنيم، به راستي نمي‌دانيم درون حافظه چه اتفاقاتي در حال وقوع است.

سخت‌افزارهاي ديجيتال معمولاً از چنين منطقي پيروي نمي‌كنند و دستورات در اين‌گونه سيستم‌ها قوانين سفت و سختي را به اجرا مي‌گذارند كه مجال اندكي براي جولان دادن داده‌ها و دستورالعمل‌ها در يك فضاي باز باقي مي‌گذارند1. به همين دليل ادوات سخت‌افزاري، انعطاف‌پذيري و كارايي محدودي دارند. سيستم‌هاي نرم‌افزاري از فلسفه متفاوتي پيروي مي‌كنند. آن‌ها به داده‌ها و دستورالعمل‌ها مجال مي‌دهند با صور گوناگون با هم تعامل داشته باشند و در يك فضاي نسبتاً باز جولان دهند و در عوض انعطاف پذيرترند و كارايي بيشتري دارند. منشأ خطا كردن نرم‌افزار همين است.

بنابراين به عنوان برنامه‌نويس اگر مايليد نرم‌افزارتان كمترين باگ را داشته باشد، بايد حافظه مورد استفاده نرم‌افزارتان را هنگام اجرا زير نظر بگيريد و ببينيد آنجا واقعاً چه خبر است. مديريت حافظه بسيار مهم است؛ نه فقط از اين جهت كه حافظه را از وجود متغيرهاي بي‌استفاده و روتين‌هاي راكد پاك كنيد، بلكه از اين جهت كه نحوه تعامل ورودي‌ها، خروجي‌ها و فرايندهاي نرم‌افزار خود را به دقت مانيتور و تماشا كنيد. حافظه كامپيوتر مانند آزمايشگاهي است كه داده‌ها و دستورالعمل‌ها را در آن به جان هم مي‌اندازيد. پس خوب است همچون شيميدانان به ارلن و بِشِر خود نگاه كنيد و ببينيد مولكول‌ها چگونه به هم واكنش نشان مي‌دهند. اميدوارم در آينده به بحث <اشكال‌زدايي از نرم‌افزار> بيشتر بپردازيم.


پي‌نوشت
1 - مگر اين‌كه به firmware يا ميان‌افزاري مجهز باشند كه در اين صورت احتمال وقوع اشتباه و خطا در كارشان بالا مي‌رود.