<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های راکب</title>
        <link>https://virgool.io/feed/@Karim</link>
        <description>چون نیک بنگری همه دکّان باز می‌کنند</description>
        <language>fa</language>
        <pubDate>2026-06-16 23:57:53</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/45523/avatar/IYfXxK.png?height=120&amp;width=120</url>
            <title>راکب</title>
            <link>https://virgool.io/@Karim</link>
        </image>

                    <item>
                <title>مهندسی معکوس فیلیمو</title>
                <link>https://virgool.io/@Karim/%D9%85%D9%87%D9%86%D8%AF%D8%B3%DB%8C-%D9%85%D8%B9%DA%A9%D9%88%D8%B3-%D9%81%DB%8C%D9%84%DB%8C%D9%85%D9%88-xikhxsplwpfj</link>
                <description>سلام!چند روزی بود که ذهنم درگیر این قضیه بود که سرویس‌هایی مثل فیلیمو، فیدیبو یا طاقچه چه جوری روی دستگاه‌های ما یه محتوا رو به ما نشون می‌دن ولی جلوی این که دست ما خارج از ساختار خود برنامه به اون محتوا برسه رو می‌گیرن.به طور دقیق‌تر برام سوال بود که من اگر یه مهندس معکوس خیلی کاربلد باشم می‌تونم از محتوای این برنامه‌ها خروجی بگیرم یا نه. برای شروع رفتم سراغ فیلیمو و تصمیم گرفتم ببینم آیا می‌تونم یه قسمت از یه سریالی رو دانلود کنم؟فقط قبل از هر چیز بگم که بدیهتا از محتوای این پست نباید سواستفاده بشه و تنها هدف من اشتراک تجربه‌م و یه خرده آموزش هست. قطعا استخراج ویدیوهای فیلیمو و انتشارشون تجاوز به حقوق ناشر محسوب می‌شه و نباید این کار رو انجام داد.البته من هم به همین دلیل سعی می‌کنم توضیحات رو جزیی بیان نکنم؛ به این امید که دوباره انجام دادن این کار اون قدرها هم بدیهی نباشه. گرچه کسی که بخواد دانلود بکنه خیلی ابزارهای حاضر و آماده‌تری براش وجود داره و هیچ وقت محتاج روش این بلاگ نمی‌شه. :دیقدم اوّل: ساده‌انگاریدر قدم اوّل توی تب Network از قسمت Developer Tools نگاه کردم موقعی که یه فیلم شروع به پخش می‌کنه چه درخواست‌هایی روی شبکه ارسال می‌شن. اوّلین چیزی که به چشم می‌اومد تعداد زیادی درخواست به فرمت &#x60;s-&lt;part_id&gt;-v1-a1.ts&#x60; بود. گفتم خب ایول این فایل‌های ts که پارت‌های مختلف فیلم هستن. کافیه فور بزنم دانلودشون کنم و کانورتشون کنم و کانکت کنم تا یه فایل mkv به دست بیارم.فایل اوّل رو دانلود کردم و اومدم با ffmpeg بررسی‌ش کنم:ffmpeg -i s-1-v1-a1.tsکه دیدم ای دل غافل! ffmpeg تشخیص نمی‌ده این فایل ts هست.نتیجه مشخّصه: این فایل‌ها رمزگذاری شدن.قدم دوم: ریزبینی در شبکهیه خرده دقیق‌تر که درخواست‌های شبکه رو نگاه کردم دیدم عه چه خوب. قبل از این که مرورگر شروع کنه به ارسال درخواست‌های ts، یه درخواستی می‌فرسته به آدرس enc.keyدانلودش کردم و دیدم محتواش یه رشته‌ی ۱۶ کاراکتریه. به نظر کلیدیه که برای رمزگشایی فایل‌های ts نیازش داریم!نکته‌ی دیگه‌ای که جالب توجّه بود initiator این درخواست‌ها بود که توی  همون تب Network قابل مشاهده‌ست. این درخواست‌ها از فایل vendors_hls شروع می‌شدن. پس اون فایل رو باز کردم و با ابزارهای خود مرورگر Pretty-print کردم و شروع به بررسی‌ش کردم. قاعدتا این کار اون قدرها هم ساده نیست چون کدهای JS روی سرور پروداکشن مینیفای(؟) می‌شن و اسم‌گذاری خیلی از متغیّرها و توابع شاید واقعا نامفهوم باشه.قدم سوم: پیدا کردن روش رمزنگاریبا بررسی کد اوّلین چیزی که فهمیدم این بود که روش رمزنگاری AES-128 هست. امّا نمی‌دونستم کدوم mode از AES. سرچ کردم معمولا از کدوم mode استفاده می‌کنن و دیدم که CBC و GCM پرکاربردترین‌هان. گفتم حالا فرض کنیم CBCـه تا بعد ببینیم چی پیش می‌آد.برای CBC باید دنبال یه بردار آغازین iv هم باشیم! دوباره با جست‌وجو در کد یه تابع به نام createInitializationVector پیدا کردم که کارش تولید همین iv بود. با breakpoint گذاشتن و این جور کارها فهمیدم که برای رمزگشایی kامین پارت فیلم، ورودی k+1 به این تابع داده می‌شه و این تابع هم این عدد رو به یه آرایه‌ی بایت ۱۶تایی تبدیل می‌کنه(به مبنای ۲۵۶ می‌بردش) تا بتونه به AES-128 ورودی بده.البته همه‌ی این‌ها حدس و گمان بود تا این که این کد پایتون رو زدم:import sys
from Crypto.Cipher import AES

def ivof(x):
    res = bytearray(16)
    for i in range(1, 5):
        res[-i] = x &amp; 255
        x &gt;&gt;= 8
    return bytes(res)

c = open(sys.argv[1], &#039;rb&#039;).read()
iv = ivof(int(sys.argv[2]))
key = open(&#039;enc.key&#039;, &#039;rb&#039;).read()
cipher = AES.new(key, AES.MODE_CBC, iv=iv)

p = cipher.decrypt(c)
with open(sys.argv[3], &#039;wb&#039;) as f:
    f.write(p)این کد موقع اجرا ۳ تا آرگومان ورودی می‌گیره: اوّلی آدرس فایل رمز شده، دومی عدد مربوط به iv و سومی هم آدرس فایل خروجی.بعد از اجرای این کد پایتون و اجرای مجدّد ffmpeg روی فایل خروجی دیدم که به‌به! این دفعه ffmpeg این فایل رو می‌شناسه. حتّی با vlc هم می‌شه این فایل رو پخش کرد.قدم چهارم: دانلود همه‌ی پارت‌هاتوی قسمت درخواست‌های شبکه، یه فایل به اسم chunk.m3u8 وجود داره که از توش می‌شه لیست آدرس پارت‌های مختلف فیلم و مدّت زمان هر کدوم رو دید. از توی اون فایل تعداد پارت‌ها رو درآوردم و چون آدرسشون نسبتا آدرس تمیز و منظّمی بود، با یه فور ساده توی bash و با wget همه‌شون رو دانلود کردم.این جا دیگه کار مهندسی معکوس تموم می‌شه. از این جا به بعدش دیگه فقط تمیزکاریه.قدم پنجم: ادغام فایل‌ها و خروجی mkvدر نهایت برای این که یه خروجی تر و تمیز داشته باشیم، بعد از دانلود پارت‌ها یه فور دیگه توی بش می‌زنیم و با اون اسکریپت پایتونمون پارت‌ها رو دیکریپت می‌کنیم.بعدش هم با روش concat demuxer که توی این لینک بهش اشاره شده فایل‌های ts رو در یک فایل ts بزرگ ادغام می‌کنیم.در نهایت هم با دستور زیر خروجی mkv می‌گیریم:ffmpeg -i merged.ts merged.mkvقدم ششم: خداحافظیممنون که تا آخر مطلب همراهی کردید. :-)اگر نظری داشتید می‌تونید نظر بدید و اگر دوست داشتید می‌تونید لایک کنید. جفتشون خوش‌حالم می‌کنن. D:</description>
                <category>راکب</category>
                <author>راکب</author>
                <pubDate>Fri, 24 Dec 2021 04:06:52 +0330</pubDate>
            </item>
                    <item>
                <title>دزدگیرسازی راکب و رکبا</title>
                <link>https://virgool.io/@Karim/%D8%AF%D8%B2%D8%AF%DA%AF%DB%8C%D8%B1%D8%B3%D8%A7%D8%B2%DB%8C-%D8%B1%D8%A7%DA%A9%D8%A8-%D9%88-%D8%B1%DA%A9%D8%A8%D8%A7-e2lfi3oyhonc</link>
                <description>سلام!قسمت اوّلچند روز پیش، یادم نیست چی شد که یه‌هو به ذهنم رسید که خودم بیام برای ماشینم دزدگیر بسازم. بعد یه خرده فکر کردم، گفتم خب اصلا چرا بعدش شرکت دزدگیرسازی نزنم؟ برای چند روز همین فکرها توی ذهنم می‌چرخیدن و می‌چرخیدن. وقتایی که بی‌کار می‌شدم در مورد طرز کار دزدگیرایی که فعلا تو بازار هستن و آژیر و سنسورای مختلف و سیستم برق‌رسانی ماشین و... می‌خوندم.اوّلاش فکر می‌کردم خیلی کار آسونی باشه. برام عجیب بود که تولید داخلی‌ش انقدر کمه. البته مطمئن نیستم کم باشه، ولی تا جایی که گشتم فقط یه شرکت ایرانی پیدا کردم که دزدگیر ماشین بزنه و اونم معمولا دزدگیرای فابریک ساده‌ی ماشین‌های داخلی رو تولید می‌کرد.همین طوری برای خودم خیال‌پردازی می‌کردم و تصوّر می‌کردم که آآآره دیگه اصلا یه ماژول بلوتوثی چیزی هم می‌ندازیم روش که حتّی با موبایل هم بشه کانفیگش کرد و... به کلّی فیچرای جذّاب براش فکر می‌کردم و این که هر سیستمش رو چه جوری پیاده‌سازی کنیم و...کم کم داشتم جدّی می‌شدم؛ ولی از یه طرف دیگه هم می‌دونستم که خب جوونم و خام... می‌دونستم که اگر واقعا بخواد همچین چیزی تولید بشه، بحث فقط پیچیدگی فنّی مداراش نیست. جزییاتی مثل حقوق کارگر، تولید بروشور، تولید بسته‌بندی، طرّاحی و تولید قاب، جزییات تولید انبوه و کللّلی چیز دیگه توی کار دخیل می‌شه.تازه اصلا فرض کنیم همه چی اوکی شد و ما هم تولید انبوه کردیم و فرستادیمش توی بازار. اکثر دزدگیرهایی که توی بازار هستن هم از آسیای شرقی وارد می‌شن و اصلا بعید نیست اون قدر برای واردکننده‌هاش ارزون تموم بشن که بتونن تو یه چشم به هم زدن این شرکت کوچولو موچولوی ما رو ورشکست کنن!خلاصه دیگه این مورد آخر کلّی نگرانم کرد. نمی‌دونستم هم چه طوری باید از جزییاتش سر دربیارم. این طور شد که دیگه دورش خط کشیدم و پرونده‌ش رو بستم و به زباله‌دان تاریخ منتقلش کردم. شاید هم دیگه هیچ وقت بهش فکر نکنم. البته نمی‌خوام بگم که «وااای لعنت به واردات که وضع تولید داخل رو کساد کرده» یا «وااای لعنت به مسئولین که اصلا به فکر تولید داخل نیستن». اصلا بحث این نیست؛ تاکسیه مگه؟قسمت دومولی داشتم واسه خودم فکر می‌کردم که الآن من کلّی برآورد قیمت کردم و کلّی چیز رو بررسی کردم و تهش هم که به نتیجه‌ای نرسیدم، همه چی نابود شد و هیچ اثری از فکرام باقی نموند.شاید دو روز دیگه، یه نفر دیگه هم همین مسیر من رو طی کنه. و دو روز بعدش یه نفر دیگه و همین طوری شاید آدم‌های مختلفی بیان و این مسیر رو طی کنن و تهش هیچ اثری از فکرای هیچ کدوممون باقی نمونه.خب یه جورایی این وسط داره یه اسراف فکر اتّفاق می‌افته. شاید اگر من یه جا یه پستی پیدا می‌کردم که یکی توش گفته بود «آره آقا جاتون خالی چند روز پیش به فکرم رسید یه شرکت دزدگیرسازی بزنم، اتّفاقا اگه فلان طور و بهمان طور تولید می‌کردم قیمت تموم شده‌ش ایکس تومن می‌شد. ولی دیگه دیدم فلان مانع و بهمان مانع جلوی راه تولیدش هستن، دیگه کلّا بی‌خیال شدم.» خب از همون روز اوّل دور قضیه رو خیط می‌کشیدم و به زندگی عادّی‌م ادامه می‌دادم. چه بسا به چیزای بدیع‌تر(!) فکر می‌کردم.حالا بحث «من» که نیست. بحث «دزدگیر» هم نیست. صرفا حرفم این بود که توی این وضعیت، به خاطر شرایط سیاسی و اقتصادی و... ممکنه هر کس به تولید یه چیزی فکر کنه. ولی این فکرها بیش از حد ایزوله هستن. شاید بشه اسمش رو گذاشت موازی‌کاری. نمی‌دونم! به هر حال. دوست داشتم یه چیزی بود که این «موازی‌کاری»ها و «دوباره‌کاری»ها رو رفع می‌کرد.قسمت سومبعد دیدم که حالا من حداقل کاری که برای شکستن این ایزولیشن(؟!) می‌تونم انجام بدم چیه؟ اوّلش به ذهنم رسید که خیلی خشک و خالی یه سایتی بزنیم، هر کس به تولید هر چی فکر کرد بیاد بنویسه. بعد دیدم که خیلی مسخره‌س! عملا می‌شه همین ویرگول. D: (نه به ذهنم نرسید که در نتیجه‌ش بیایم تو ویرگول تجارب ناموفّقمون از تولید رو بنویسیم که بقیّه بتونن بخوننش!)بعد همین طوری توی تخیّلات خودم غرق شدم و شروع کردم به ایده‌پرونی. بعد گفتم که بیایم یه گراف جهت‌دار بسازیم. هر راسش یه کالا باشه، هر یال از راس u به راس v نشون‌دهنده‌ی این باشه که برای تولید کالای v به کالای u نیاز هست. مثلا برای تولید کالای «کتاب» به «کاغذ»، «پرینتر» و «چسب» نیاز داریم، پس از رئوس مربوط به «کاغذ» و «پرینتر» و «چسب» یه یال جهت‌دار به راس «کتاب» می‌کشیم.بعد حالا این گرافه رو تولید کنیم با یه بدبختی، ملّت تو یه ساختار تعاملی مثل ویکی‌پدیا بیان و تجاربشون از تولید هر کالا رو توی اون راس بنویسن. (قاعدتا هر راس(کالا) یه پیجی براش خودش داره دیگه.) خود گراف هم که یه شهودی به ملّت می‌ده که اصلا باید خرج چه کالاهایی رو موقع تولید مد نظر داشته باشن و...بعد ولی بازم هنوز چرت بود به نظرم. چون ما بین «روش‌های تولید» تمایزی قائل نمی‌شیم! چه می‌دونم مثلا(تاکید می‌کنم مثلا D:) ما برای تولید رب، می‌تونیم گوجه‌ها رو اوّل بریزیم توی مخلوط کن. می‌تونیم هم بریزیم توی یه دیگ بزرگ و بعد یه الاغ بذاریم توی اون دیگ که گوجه‌ها رو لگد کنه. خب توی روش اوّل پیش‌نیاز کالای «رب»، کالای «مخلوط‌کن» هست، توی روش دوم هم «الاغ». امّا ناظر خارجی،‌ چیزی که از گراف می‌بینه اینه که برای تولید رب به مخلوط‌کن «و» الاغ نیاز هست. شاید نفهمه که به مخلوط‌کن «یا» الاغ نیاز هست.گذشته از اون شاید طی فرآیند تولید یه کالا، کالاهای جانبی‌ای هم به دست بیان ولی توی این گراف خوب نمایش داده نمی‌شن.بعد گفتم خب عب نداره می‌آییم توی گرافمون، یه سری راس جدید اضافه می‌کنیم، این راس‌ها دیگه کالا نباشن، اینا «روش تولید» باشن. بعد این طوری بشه که هر «روش تولید» یه سری کالا ورودی می‌گیره، یه سری کالا خروجی می‌ده. مثلا یه فرآیند «تولید بدنه‌ی ماشین»، یه مقدار «ورقه‌ی فلزی» و «دستگاه جوش» ورودی می‌گیره، در نهایت «بدنه‌ی ماشین» و «ضایعات فلزی» تحویل می‌ده.پس یالای گرافمون دیگه «از کالا به کالا» نخواهند بود. یال‌هامون تبدیل می‌شن به یال‌های «از کالا به روش تولید» یا «از روش تولید به کالا» که دسته‌ی اوّل، نشون‌دهنده‌ی پیش‌نیازهای اون روش تولید هستن و دسته‌ی دوم نشون‌دهنده‌ی خروجی‌های اون فرآیند.این طوری دیگه مطالب ملّت هم ساختار یافته‌تر می‌شه. هر کی بخواد چیزی بنویسه، باید یه «روش تولید» اضافه کنه؛ نه مثل حالت قبل، یه پست عادّی توی صفحه‌ی مربوط به یه کالا(مثل فروم‌ها). تازه اگر یه تولیدکننده‌ای نیاز به یه کالای ایکس داشته باشه، می‌تونه بیاد توی این گراف و ببینه این ایکس، خروجی چه فرآیندهایی هست؛ چه بسا توی یه فرآیندی به صورت زباله تولید بشه و با قیمت خیلی ارزون بتونه از یه کارخونه که اون روش تولید رو استفاده می‌کنه، بخردش! (توی همین مثال بالا، یه نفر شاید بتونه آهن‌پاره رو خیلی ارزون از کارخونه‌ی تولید بدنه‌ی ماشین بخره؛ البته که اینا صرفا مثال هستن!)حالا البته یه سری ریزه‌کاری هم داره دیگه. مثلا کالاها خیلی انواع مختلفی دارن؛ قاعدتا نباید به کالای سرمایه‌ای و کالای مصرفی به شکل یکسانی نگاه بشه! یا مثلا بعضی از روش‌های تولید به درد تولید صنعتی و انبوه نمی‌خورن و بیشتر از جنس این پروژه‌های DIY(Do it yourself) هستن. ولی حالا سعی کردم کلّیّات رو بگم!قسمت چهارمولی خب حالا خودمونیم! همه چی هم صرفا وجود داشتن یه ساز و کار ایده‌آل که نیست. یه دلیلی باید وجود داشته باشه که ملّت به سمت این ساز و کار سوق پیدا کنن. تازه کسی هم اگر یه روش خوب برای تولید یه چیزی پیدا کنه، خب می‌ره برای خودش تولید می‌کنه و پول‌دار می‌شه؛ نه این که بیاد اوپن سورس بذاردش توی همچین سامانه‌ای تا بقیّه بتونن ببیننش! اگر بخواد خیلی غنی بشه این سایته، قطعا پر از تجارب «شکست خورده» خواهد بود. که در نوع خودشون باارزشن امّا به جذّابیّت موارد موفّق نخواهند بود. :))ولی نمی‌دونم. یه مقدار هم اصلا این فکرم از سمت این فضای اوپن سورس و... می‌آد. این که اوّل توی فضای نرم‌افزار جنبش نرم‌افزار متن‌باز و نرم‌افزار آزاد راه افتاد و یه مدّت بعد هم طبق خاصیت مویینگی به فضای سخت‌افزار کشیده شد. توی تصوّراتم بامزه بود که این فرهنگه به کل فضای صنعت تعمیم پیدا بکنه.حالت اکستریمش اینه که تکنولوژی تولید بمب اتم هم در دسترس عموم باشه. XDالبته فکر کنم لزوما تنها مشکل هر چیزی توی فرآیند تولید، دسترسی به «تکنولوژی» نباشه. شاید خیلی جاها بحث مقرون به صرفه بودن مطرح باشه مثل حقوق کارگر یا... امّا به هر حال! خود این بحث گره عمیقی می‌خوره به اقتصاد لابد. منم که اقتصاددان نیستم. یه آدمی هستم که به خاطر سر رفتن حوصله نشسته پشت کیبورد داره تو ویرگول تز می‌ده. D:بگذریم حالا. اینا که تخیّلات من بود راجع به این مشکله. ولی واقعا دوست دارم بدونم این مشکله تو جاهای دیگه چه طوری رفع می‌شه یا کسی ایده‌ای داره بگه کلّا.همین دیگه... و من الله التوفیق.</description>
                <category>راکب</category>
                <author>راکب</author>
                <pubDate>Sun, 21 Jun 2020 17:28:07 +0430</pubDate>
            </item>
                    <item>
                <title>آموزش GDB - قسمت ۱</title>
                <link>https://virgool.io/@Karim/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-gdb-%D9%82%D8%B3%D9%85%D8%AA-%DB%B1-kthgksap9grt</link>
                <description>سلام!توی قسمت ۰، با چند تا دستور ابتدایی gdb کمی آشنا شدیم. حالا توی این بخش، چند تا دستور جدید یاد می‌گیریم! این دفعه قسمت بزرگی از هدفمون اینه که بتونیم با توابعی که صدا زده می‌شن هم کلنجار بریم! با درس‌های قسمت قبل، همچین کاری غیرممکن یا سخت بود. D:درس منفی یکم: چند تا نکته‌ی کوچولواکثر وقت‌ها لازم نیست که ما شکل کامل دستوری که می‌خوایم رو تایپ کنیم. خیلی وقت‌ها تایپ کردن یه پیشوند از اون دستور که به صورت یکتا مشخّصش کنه یا عبارات مخفّفی که شناخته شدن، کار همون دستورات رو برامون انجام می‌دن. برای مثال، استفاده از b به جای break و یا استفاده از bt به جای backtrace.خب حالا برای امروز، با این کد(code01.c) قراره کار کنیم:#include &lt;assert.h&gt;

int fib(int n)
{
    assert(n &gt;= 5); 
    if (n &lt;= 1)
        return n;
    int x = fib(n - 1); 
    int y = fib(n - 2); 
    return x + y;
}

int main()
{
    fib(10);
    return 0;
}همون طور که از ظاهر کد برمی‌آد، این کد قراره تابع فیبوناچی رو برامون محاسبه کنه. تنها نکته‌ش اینه که وقتی توی روند بازگشتی تابع fib، تابع به ازای nهای کم‌تر از ۵ صدا زده بشه، مقدار ورودی assert برابر با false می‌شه و برنامه به نوعی کرش می‌کنه.برای کامپایلش هم مثل قسمت قبل:gcc -g code01.c -o exec01درس صفرم: fileخب قبلا برای دیباگ کردن همچین برنامه‌ای توی ترمینال می‌نوشتیم gdb exec01. الآن هم این کار رو می‌تونیم بکنیم. یکی از روش‌های دیگه، اینه که توی ترمینال فقط دستور gdb رو اجرا کنیم، بدون هیچ آرگومانی. در این صورت وارد فضای gdb می‌شیم و بعدش با دستور file، می‌تونیم مشخّص کنیم که قصدمون دیباگ کردن چه برنامه‌ای هست:(gdb) file exec01 
Reading symbols from exec01...done.
(gdb)حالا به راحتی برنامه رو با run اجرا می‌کنیم و می‌بینیم که:(gdb) run
Starting program: /home/rakeb/tmp/exec01 
exec01: code01.c:5: fib: Assertion `n &gt;= 5&#039; failed.

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51	../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb)که به زبون آدمی‌زادی می‌گه آقا این برنامه‌ی شما توی یه Assertion که شرطش n &gt;= 5 بوده فِیل شده و بعد از اون، سیگنال SIGABRT برای برنامه فرستاده شده.الآن برنامه‌مون هنوز خارج نشده! رفتار gdb با سیگنال‌هایی که مربوط به یه اروری می‌شن، مثل رفتارش با breakpointهاست! چون ممکنه برنامه نویس نیاز داشته باشه تا حول اون وضعیتی که منجر به کرش کردن شده، یه خرده چرخ بزنه و اطّلاعات به دست بیاره.به هر حال، می‌تونیم با دستور next یا kill(درس نیمم؟!) به اجرای برنامه خاتمه بدیم. ولی فعلا این کار رو نمی‌کنیم تا با backtrace آشنا بشیم.درس یکم: backtraceاگر با پایتون کار کرده باشید، می‌دونید که وقتی به یه ارور می‌خوره و کرش می‌کنه، کل روندی که توابع هم دیگه رو صدا کردن تا به اون نقطه‌ی ایجاد خطا برسن رو چاپ می‌کنه براتون. اوّلش هم همچین چیزی می‌نویسه:Traceback (most recent call last):خب این اطّلاعات به دیباگ برنامه خیلی کمک می‌کنه. توی gdb هم همچین چیزی داریم. بلافاصله بعد از کرش کردن برنامه‌مون اگر دستور backtrace یا bt رو وارد کنیم، می‌بینیم که روند صدا شدن توابع به ترتیب از main تا این نقطه‌ای که برنامه کرش کرده رو برامون می‌نویسه:(gdb) backtrace 
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff7a24801 in __GI_abort () at abort.c:79
#2  0x00007ffff7a1439a in __assert_fail_base (fmt=0x7ffff7b9b7d8 &amp;quot%s%s%s:%u: %s%sAssertion `%s&#039; failed.\n%n&amp;quot, assertion=assertion@entry=0x55555555475d &amp;quotn &gt;= 5&amp;quot, file=file@entry=0x555555554754 &amp;quotcode01.c&amp;quot, line=line@entry=5, function=function@entry=0x555555554764 &lt;__PRETTY_FUNCTION__.1809&gt; &amp;quotfib&amp;quot) at assert.c:92
#3  0x00007ffff7a14412 in __GI___assert_fail (assertion=0x55555555475d &amp;quotn &gt;= 5&amp;quot, file=0x555555554754 &amp;quotcode01.c&amp;quot, line=5, function=0x555555554764 &lt;__PRETTY_FUNCTION__.1809&gt; &amp;quotfib&amp;quot) at assert.c:101
#4  0x000055555555467a in fib (n=4) at code01.c:5
#5  0x0000555555554692 in fib (n=5) at code01.c:8
#6  0x0000555555554692 in fib (n=6) at code01.c:8
#7  0x0000555555554692 in fib (n=7) at code01.c:8
#8  0x0000555555554692 in fib (n=8) at code01.c:8
#9  0x0000555555554692 in fib (n=9) at code01.c:8
#10 0x0000555555554692 in fib (n=10) at code01.c:8
#11 0x00005555555546bd in main () at code01.c:15
(gdb)از پایین لیست به سمت بالای لیست، مسیر رسیدن از main به عامل کرش رو می‌تونیم ببینیم. واضحه که خطوط ۴ تا ۱۱ مربوط به برنامه‌ی شما هستن و خطوط ۰ تا ۳ توسّط تابع assert فراخوانی شدن. حالا با دستور kill برنامه رو می‌بندیم تا یه بار دیگه اجراش کنیم.درس دوم: break ifحالا می‌خوایم یه breakpoint روی نقطه‌ی شروع تابع fib قرار بدیم تا وقتی که با n=5 صدا زده شد، روند اجرای برنامه رو دنبال کنیم. یک راهش اینه که خیلی ساده بنویسیم:break fibبا این کار برنامه دقیقا لحظه‌ای که می‌خواد وارد این تابع بشه، متوقّف می‌شه و ما می‌تونیم با استفاده از این دستور و دستور continue اون قدر ادامه بدیم تا به n=5 برسیم. امّا راه ساده‌تری که وجود داره، استفاده از breakpointهای شرطیه. خیلی ساده می‌نویسیم:(gdb) break fib if n == 5
Breakpoint 1 at 0x555555554655: file code01.c, line 5.
(gdb) run
Starting program: /home/rakeb/tmp/exec01 

Breakpoint 1, fib (n=5) at code01.c:5
5	    assert(n &gt;= 5);
(gdb)می‌بینید؟ دقیقا پشت اوّلین دستور از تابع وقتی که ورودی‌ش n=5 هست برنامه متوقّف شده. این جا هم می‌تونیم دستور bt رو بزنیم تا روند صدا شدن توابع رو ببینیم.(gdb) bt
#0  fib (n=5) at code01.c:5
#1  0x0000555555554692 in fib (n=6) at code01.c:8
#2  0x0000555555554692 in fib (n=7) at code01.c:8
#3  0x0000555555554692 in fib (n=8) at code01.c:8
#4  0x0000555555554692 in fib (n=9) at code01.c:8
#5  0x0000555555554692 in fib (n=10) at code01.c:8
#6  0x00005555555546bd in main () at code01.c:15
(gdb)خب حالا فرض کنید می‌خوایم قبل از ادامه‌ی برنامه، یه مقدار پرسه بزنیم توی این توابع و مقدار بعضی متغیّرها رو با دستور print نگاه کنیم! خب اگر این متغیّرها توی همین تابع فعلی باشن که اوکیه، ولی اگر توی فریم توابع بالادستی باشن چی؟ واضحه که همین طوری نمی‌تونیم مقدار n به ازای فراخوانی تابع مربوط به خط #3 رو نگاه کنیم! (حالا این جا n ورودی تابع هست و بدیهیه مقدارش چنده. امّا تو موارد مختلف، به جای این n می‌شه هر متغیّر لوکالی مد نظرمون باشه که مقدارش توی backtrace قابل مشاهده نباشه.درس سوم: up/downمفهوم این ۲ تا دستور خیلی ساده هست! با دستور up، می‌رید توی تابعی که تابع فعلی رو صدا زده. یعنی اگر توی تابع مربوط به خط iام از خروجی backtrace باشید، منتقل می‌شید به خط i+1. دستور down هم خلاف این کار رو می‌کنه و منتقل می‌شید به خط i-1! دقّت کنید که این منتقل شدن، هیچ تاثیری توی روند اجرای برنامه نداره. صرفا شما به صورت مجازی به اون جا منتقل می‌شید تا بتونید مقدار توابع رو نگاه کنید و تغییر بدید.لازم به ذکره که این ۲ تا دستور، می‌تونن یه آرگومان اختیاری هم بگیرن که بفهمن به جای یک لایه، چند لایه برن بالا یا پایین. مثلا دستور up 5 شما رو منتقل می‌کنه به خط i + 5.(gdb) up
#1  0x0000555555554692 in fib (n=6) at code01.c:8
8	    int x = fib(n - 1);
(gdb) print n
$1 = 6
(gdb) up 2
#3  0x0000555555554692 in fib (n=8) at code01.c:8
8	    int x = fib(n - 1);
(gdb) print n
$2 = 8
(gdb) down 
#2  0x0000555555554692 in fib (n=7) at code01.c:8
8	    int x = fib(n - 1);
(gdb) down
#1  0x0000555555554692 in fib (n=6) at code01.c:8
8	    int x = fib(n - 1);
(gdb) down
#0  fib (n=5) at code01.c:5
5	    assert(n &gt;= 5);
(gdb) down
Bottom (innermost) frame selected; you cannot go down.
(gdb)حالا می‌خوایم با یه روش کاملا احمقانه، کاری کنیم که برنامه‌مون وقتی n&lt;=4 هست، به خط assert نرسه. برای این کار می‌خوایم با یه روش مشابه، یه breakpoint روی ورودی تابع fib به ازای زمان‌هایی که n&lt;=4 هست بذاریم و توی این حالات، قبل از این که روند اجرای برنامه به assert برسه، خودمون دستی مقدار واقعی fib(n) رو return کنیم!درس چهارم: info breakpointsبا این دستور می‌تونیم اطّلاعات breakpointهایی که توی برنامه‌مون وجود دارن رو ببینیم! در مورد همین مثال:(gdb) info breakpoints
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000555555554655 in fib at code01.c:5
	stop only if n == 5
	breakpoint already hit 1 time
(gdb)همون طور که می‌بینید، اطّلاعات مختلفی رو در مورد همون یه breakpoint که توی برنامه داشتیم چاپ کرده. خصوصیتی که در ادامه باهاش کار داریم، Enb یا Enabled هست.لازم به ذکره اگر breakpointهای بیشتری هم وجود داشته باشن، توی این لیست با Numهای مختلف نشون داده می‌شن.درس پنجم: enable/disable/remove breakpointsما می‌تونیم breakpointها رو غیرفعّال کنیم و یا پاکشون کنیم! برای غیر فعّال کردنشون کافیه از دستور زیر استفاده کنیم:(gdb) disable breakpoints 1که عدد ۱ در انتهای دستور، نشان‌دهنده‌ی Num مربوط به breakpointی هستش که می‌خوایم غیرفعّالش کنیم.به طریق مشابه با استفاده از دستور enable به جای disable می‌تونیم یک breakpoint رو فعّال کنیم و با استفاده از delete به جای disable می‌تونیم اون breakpoint رو پاک کنیم. تغییراتی که این دستورات اعمال می‌کنن رو به راحتی با دستور info breakpoints می‌تونیم مشاهده کنیم.حالا برای این که اون روش احمقانه مربوط به جلوگیری از کرش کردن برنامه رو پیاده‌سازی کنیم، کافیه اوّل دستورات زیر رو اجرا کنیم:(gdb) break fib if n &lt;= 4
Breakpoint 2 at 0x555555554655: file code01.c, line 5.
(gdb) run
Starting program: /home/rakeb/tmp/exec01 

Breakpoint 2, fib (n=4) at code01.c:5
5	    assert(n &gt;= 5);
(gdb)درس ششم: returnخب می‌دونیم که خروجی fib به ازای n=4 باید برابر با ۳ و به ازای n=3 باید برابر با ۲ باشه. برای همین کافیه هر وقت breakpointمون به fib(3) رسید از دستور return 2 و هر وقت به fib(4) رسید از دستور return 3 استفاده کنیم.البته تابع fib(3) و fib(4) دفعات زیادی صدا خواهد شد و ما در نهایت پیر می‌شیم تا اون دستورات رو اجرا کنیم! این جا صرفا می‌خواستم بگم از لحاظ تئوری این کار ممکنه. :)) اگر خیلی مُصر هستید که این کار رو تجربه کنید، می‌تونید توی برنامه‌ی code01.c به جای فراخوانی fib(10)، تابع fib رو به ازای یک n کوچک‌تر فراخوانی کنید و یا موقعی که breakpoint می‌ذارید، به جای قرار دادن n&lt;=4 از یه عدد بزرگتر استفاده کنید تا دفعات کم‌تری به اون breakpoint برسیم.درس هفتم: listحالا که تا این جای کار اومدیم، بیاید با چند تا دستور دیگه هم آشنا بشیم! ولی قبل از هر چیز، خط مربوط به assert رو از برنامه پاک کنید و برنامه رو مجدّدا اجرا کنید. مثل حالت قبل، یه breakpoint روی تابع fib به ازای حالاتی که n=5 هست می‌ذاریم!(gdb) break fib if n == 5
Breakpoint 1 at 0x605: file code01.c, line 5.
(gdb) run
Starting program: /home/rakeb/tmp/exec01 

Breakpoint 1, fib (n=5) at code01.c:5
9	    if (n &lt;= 1)
(gdb) این جا می‌خوایم ببینیم سورس کد برنامه‌مون چه شکلیه! برای این کار به راحتی دستور list رو اجرا می‌کنیم:(gdb) list
4	
5	
6	
7	int fib(int n)
8	{
9	    if (n &lt;= 1)
10	        return n;
11	    int x = fib(n - 1);
12	    int y = fib(n - 2);
13	    return x + y;
(gdb) list
14	}
15	
16	int main()
17	{
18	    fib(10);
19	    return 0;
20	}
(gdb) list 0
1	#include &lt;assert.h&gt;
2	
3	
4	
5	
6	
7	int fib(int n)
8	{
9	    if (n &lt;= 1)
10	        return n;
(gdb) list
11	    int x = fib(n - 1);
12	    int y = fib(n - 2);
13	    return x + y;
14	}
15	
16	int main()
17	{
18	    fib(10);
19	    return 0;
20	}
(gdb) list
Line number 21 out of range; code01.c has 20 lines.
(gdb)در واقع دستور list، دفعه‌ی اوّل که اجرا می‌شه ۱۰ خط از برنامه‌مون(با مرکزیت خط فعلی!) رو چاپ می‌کنه. دفعه‌ی بعد، ۱۰ خط بعدی رو چاپ می‌کنه و همین طور ادامه می‌ده تا به پایان برنامه برسه. این دستور البته یه آرگومان اختیاری هم می‌تونه دریافت کنه. این آرگومان، به نوعی بهش می‌گه از چه خطی شروع به نمایش کد کنه و ادامه بده.در کل این دستور خیلی چیز پیچیده‌ای نیست و با یه خرده بازی کردن باهاش می‌شه طرز کارش رو فهمید. D:درس هشتم: stepاین دستور خیلی کاربردیه!! طرز عملکردش خیلی شبیه دستور next هست. با این تفاوت که وقتی به فراخوانی تابعی می‌رسیم و next رو اجرا می‌کنیم، از پیچدگی‌های درونی اون تابع می‌گذره و در واقع توی همین تابع فعلی می‌مونه و سراغ دستور بعد از اون فراخوانی می‌ره. امّا step این طوری نیست و موقعی که قبل از فراخوانی یه تابع اجراش می‌کنیم، وارد اون تابع می‌شه. مثلا توی همین نقطه‌ای که برنامه‌مون وایساده، چند بار اجراش می‌کنیم!(gdb) step
11	    int x = fib(n - 1);
(gdb) step
fib (n=4) at code01.c:9
9	    if (n &lt;= 1)
(gdb) bt
#0  fib (n=4) at code01.c:9
#1  0x000055555555461d in fib (n=5) at code01.c:11
#2  0x000055555555461d in fib (n=6) at code01.c:11
#3  0x000055555555461d in fib (n=7) at code01.c:11
#4  0x000055555555461d in fib (n=8) at code01.c:11
#5  0x000055555555461d in fib (n=9) at code01.c:11
#6  0x000055555555461d in fib (n=10) at code01.c:11
#7  0x0000555555554648 in main () at code01.c:18
(gdb) step
11	    int x = fib(n - 1);
(gdb)همون طور که می‌بینید، به نقطه‌ی فراخوانی fib(n-1) می‌رسیم و با اجرای دستور step، از frame کنونی که fib(5) بود، وارد fib(4) شدیم.مثل دستورات up، down و next که  یه آرگومان اختیاری می‌گرفتن که تعداد دفعات تکرارشون بود، می‌تونیم یه آرگومان به step بدیم و به جای این که یک مرحله یک مرحله جلو بریم، چند مرحله چند مرحله جلو بریم! مثلا به جای این ۲ تا step که توی همین مثال اجرا کردیم، می‌تونستیم بزنیم step 2 و کارمون کمی خلاصه‌تر بشه.درس نهم: finishاین دستور در کنار دستور step خیلی به کار می‌آد. وقتی وسط اجرای یه تابعی هستیم، دستور finish باعث می‌شه برنامه تا آخر این تابع ادامه پیدا کنه و در نهایت به جایی که این تابع فراخوانی شده برگردیم. البته به شرطی که این وسط به breakpointی گیر نکنیم. D:توی همین مثال فعلی‌مون، برای این که به fib(5) برگردیم:(gdb) finish
Run till exit from #0  fib (n=4) at code01.c:11
0x000055555555461d in fib (n=5) at code01.c:11
11	    int x = fib(n - 1);
Value returned is $1 = 3
(gdb) step
12	    int y = fib(n - 2);
(gdb) print x
$2 = 3
(gdb)همون طور که خود gdb هم بهمون گفت، اجرای fib(4) تموم شد و به نقطه‌ای که این تابع فراخوانی شده بود برگشتیم. یعنی خط ۱۱م برنامه توی fib(5).خب... این سری از آموزش gdb رو هم توی این نقطه تموم می‌کنیم. ولی یادتون باشه با استفاده از دستور help توی gdb می‌تونید هر اطّلاعاتی خواستید راجع به دستورات مختلف به دست بیارید و کاستی‌های این آموزش رو جبران کنید. در واقع هدف من از این آموزش‌ها اینه که کسایی که مشتاق هستن، با اصول اوّلیه‌ی gdb آشنا بشن تا بتونن بعدا با سرچ و help و... راحت‌تر گلیم خودشون رو از آب بیرون بکشن.وگرنه، نه من اون قدری gdb بلد هستم که بخوام کلّش رو یاد بدم، نه کل gdb نیاز همه‌ی آدم‌ها می‌شه و نه اصلا gdb اون قدری دنیای کوچیکی داره که حتّی اگر من بلد بودم، حوصله داشتم در موردش بنویسم. :))همین دیگه...و من الله التوفیق!</description>
                <category>راکب</category>
                <author>راکب</author>
                <pubDate>Mon, 27 Apr 2020 14:25:04 +0430</pubDate>
            </item>
                    <item>
                <title>آموزش GDB - قسمت ۰</title>
                <link>https://virgool.io/coderlife/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-gdb-%D9%82%D8%B3%D9%85%D8%AA-%DB%B0-f1wnsz3pyvjk</link>
                <description>توی این پست قراره سری به gdb بزنیم و کارهای پایه‌ای‌ش رو یاد بگیریم!همون طور که احتمالا می‌دونید، gdb رو به عنوان gnu debugger هم می‌شناسن. در اصل این برنامه برای دیباگ برنامه‌هایی که ما نوشتیم استفاده می‌شه و از زبان‌های مختلفی پشتیبانی می‌کنه. توی این آموزش با زبان C کار رو پیش می‌برم، ولی سعیم بر این هست که چیزایی که می‌گم اون قدر جزیی نباشن تا به درد سایر زبان‌ها نخورن.راستی همین اوّل، اگر می‌خواید ببینید gdb از چه زبان‌هایی پشتیبانی می‌کنه، یه راه دم دستی‌ش اینه که توی ترمینالتون بزنید gdb تا برنامه اجرا بشه و بعد توی شل برنامه تایپ کنید set language و دو سه بار TAB رو فشار بدید تا یه لیست از زبان‌هایی که به طور پیش‌فرض پشتیبانی می‌کنه رو چاپ کنه!درس صفرم: کامپایل و اجرای برنامهخب برای شروع یه برنامه می‌نویسیم و کامپایلش می‌کنیم تا با gdb اجراش کنیم. فرض کنید همچین کدی رو توی فایلی به اسم code00.c می‌نویسم:#include &lt;stdio.h&gt;

int main() {
    char name[10];
    printf(&amp;quotEnter your name: &amp;quot);
    scanf(&amp;quot%s&amp;quot, name);
    printf(&amp;quotSalam %s\n&amp;quot, name);
    return 0;
}همون طور که می‌بینید کد ساده‌س؛ صرفا یک اسمی رو از ورودی می‌خونه و بهش سلام می‌کنه. کامپایل کردن برنامه‌ها در حالت عادّی با دستوری مثل دستور زیر انجام می‌شه:gcc code00.c -o exec00امّا موقعی که قراره برنامه‌مون رو با gdb اجرا کنیم، یک فلگ به این دستور اضافه می‌شه و به شکل زیر باید برنامه رو کامپایل کنیم:gcc -g code00.c -o exec00کار این فلگ در واقع اضافه کردن یک سری نماد به فایل اجرایی هست تا gdb بتونه به خوبی با اون فایل اجرایی کار بکنه. حالا برای اجرای gdb کافیه توی ترمینال این دستور رو اجرا کنیم:gdb exec00حالا وارد محیط gdb شدیم! امّا چه طور برنامه رو اجرا و دیباگ کنیم؟!درس اوّل: runخب! اوّلین دستوری که باهاش آشنا می‌شیم دستور run هست. این دستور خیلی ساده برنامه‌مون رو در محیط gdb اجرا می‌کنه. الآن اگر این دستور رو وارد کنیم، می‌بینیم برنامه منتظر می‌مونه تا ما اسممون رو وارد کنیم! اگر اسممون رو وارد کنیم و Enter بزنیم، برنامه بهمون سلام می‌کنه و کنترل محیط دوباره به دست پوسته‌ی(شل) gdb برمی‌گرده.(gdb) run
Starting program: /home/rakeb/tmp/exec00 
Enter your name: rakeb
Salam rakeb
[Inferior 1 (process 17536) exited normally]
(gdb) اگر چندین و چند بار دیگه دستور run رو وارد کنیم هم چنین اتّفاقی می‌افته.درس دوم: break pointاحتمالا اوّلین انتظاری که از یک دیباگر داریم، این هست که اجازه بده توی نقاطی از کد، break point بذاریم تا وقتی روند اجرای برنامه به این نقاط رسید، متوقّف بشه و بتونیم مقدار متغیّرهای مختلف و... رو چک کنیم. این کار توی gdb به راحتی انجام می‌شه. break pointها رو می‌تونیم روی خط مشخّصی از سورس کد برنامه و یا روی اسم یک تابع قرار بدیم. مثلا فرض کنید می‌خوایم توی همین برنامه‌ی بالا، ابتدای خطی که تابع scanf قرار داره یک break point قرار بدیم! به راحتی دستور زیر رو می‌زنیم:(gdb) break code00.c:6عدد ۶ به خاطر این هست که دستور scanf توی ۶امین خط از کد ما نوشته شده.البته این روش بیشتر به درد پروژه‌های بزرگ می‌خوره که از بیش از یک فایل تشکیل شدن؛ این جا چون تنها فایل برنامه‌ی ما code00.c هست، این دستور هم مثل دستور قبل عمل می‌کنه:(gdb) break 6حالا اگر دستور run رو وارد کنیم، چنین اتّفاقی می‌افته:(gdb) run
Starting program: /home/rakeb/tmp/exec00 

Breakpoint 2, main () at code00.c:6
6	    scanf(&amp;quot%s&amp;quot, name);
(gdb)همچین چیزی یعنی این که برنامه توی این خط از کد متوقّف شده. حالا یه سری سوال پیش می‌آد! چه جوری مقدار متغیّرها رو چک کنیم؟ چه طوری برنامه رو ادامه بدیم؟ و...درس سوم: continueاوّل از همه باید گفت که به راحتی با دستور continue می‌تونیم اجرای برنامه رو از همون نقطه‌ای که متوقّف شده بود ادامه بدیم:(gdb) continue
Continuing.
Enter your name: Rakeb
Salam Rakeb
[Inferior 1 (process 17677) exited normally]
(gdb) درس چهارم: nextامّا عجله نکنید! هنوز برای چاپ کردن مقدار متغیّرها زوده چون هنوز به متغیّری مقداری ندادیم که بخوایم چاپش کنیم. :دیالآن می‌خوایم اجرای این دستوری که قبلش وایسادیم(scanf) تموم بشه تا بتونیم مقدار رشته‌ی name رو چاپ کنیم. این کار رو با دستور next انجام می‌دیم:(gdb) run
Starting program: /home/rakeb/tmp/exec00 

Breakpoint 2, main () at code00.c:6
6	    scanf(&amp;quot%s&amp;quot, name);
(gdb) next
Enter your name: Rakeb
7	    printf(&amp;quotSalam %s\n&amp;quot, name);
(gdb)خب همون طوری که می‌بینید، دستور scanf اجرا می‌شه و برنامه سر دستور بعدی، یعنی printf دوباره متوقّف می‌شه.این جا یه نکته‌ی کنکوری هم داشت؛ این که همون طور که می‌بینید break pointهامون بعد از اجرای برنامه پاک نمی‌شن و همچنان باقی می‌مونن.درس پنجم: printحالا با دستور print به راحتی می‌تونیم مقدار خونه‌های آرایه‌ی name رو چاپ کنیم!(gdb) print name
$1 = &amp;quotRakeb\000\377\177\000&amp;quot
(gdb)می‌بینیم که این دستور مقادیر آرایه‌ی name رو چاپ می‌کنه. اعدادی که قبلشون \ وجود داره هم در واقع در مبنای ۸ نوشته شدن. مثلا عدد داخل اندیس ۵ رشته، برابر با ۰ هست که نشان‌دهنده‌ی انتهای رشته‌س! درس ششم: setبه عنوان آخرین درس از این مطلب، به معرّفی اختصاری دستور set برای تغییر مقدار متغیّرها می‌پردازیم. تصوّر کنید در ادامه‌ی روند دیباگ، تصمیم می‌گیری که اسم به جای Rakeb چیز دیگری باشد، مثلا Arshia! در این حالت به راحتی می‌نویسیم:(gdb) set name = &amp;quotArshia&amp;quotو مثلا در ادامه تصمیم می‌گیریم که حرف آخر Arshia هم بزرگ باشد. به طریق مشابه:(gdb) set name[5] = &#039;A&#039;سپس با ادامه‌ی برنامه، می‌بینیم که تغییرات ما به خوبی در برنامه اعمال شده‌اند:(gdb) continue 
Continuing.
Salam ArshiA
[Inferior 1 (process 18107) exited normally]
(gdb) درس شش و نیمم: quitدر نهایت نیز برای خروج از gdb کافی است از دستور quit و یا Ctrl+d استفاده کنید!در این مطلب تلاش شد تا محتوایی برای آشنایی مقدّماتی با gdb ارائه شود. در مطالب بعدی، تلاش بر این خواهد بود که دستورات کاربردی دیگری بیان شود و البته کاربردهای دیگری از این دستورات نیز به نمایش گذاشته شود.موفّق باشید! D:</description>
                <category>راکب</category>
                <author>راکب</author>
                <pubDate>Sat, 18 Apr 2020 22:14:57 +0430</pubDate>
            </item>
                    <item>
                <title>فیلد چندگزینه‌ای پویا در Django REST</title>
                <link>https://virgool.io/@Karim/%D9%81%DB%8C%D9%84%D8%AF-%DA%86%D9%86%D8%AF%DA%AF%D8%B2%DB%8C%D9%86%D9%87%D8%A7%DB%8C-%D9%BE%D9%88%DB%8C%D8%A7-%D8%AF%D8%B1-django-rest-nehv2ixbssky</link>
                <description>سلام!اوّل از همه! این اوّلین پست من توی ویرگوله. و درست نمی‌دونم که چه جوری می‌شه باهاش کار کرد و چیزایی مثل سینتکس هایلایتینگ و... به قضیه اضافه کرد. حتّی اصلا نمی‌دونم چنین کاری ممکنه یا نه. :-بابابزرگبریم سراغ اصل مطلب!اگر کمی تجربه‌ی برنامه‌نویسی با جنگو داشته باشید، حتما می‌دونید که بعضی وقت‌ها ما توی مدل‌هامون یک فیلد داریم که می‌خوایم چند تا گزینه‌ی محدود داشته باشه. خب توی چنین شرایطی از آرگومان &#x60;choices&#x60; استفاده می‌کنیم.از اون جایی که مثال‌ها به درک مطلب خیلی کمک می‌کنن، بیاید با مدل «دانشجو» و فیلد «دانشگاه» مثال بزنیم!class Student(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    university = models.CharField(
        max_length=100,
        choices=(
            (&#039;UT&#039;, _(&#039;University of Tehran&#039;)),
            (&#039;SUT&#039;, _(&#039;Sharif University of Technology&#039;)),
        ),
    )خب... توی همین مثال، تصوّر کنید ما بخوایم لیست همه‌ی دانشگاه‌های کشور رو وسط تعریف مدل‌هامون اضافه کنیم... فکر می‌کنم شدّت کثافت(!) این روش بر کسی پوشیده نباشه.حالا ممکنه ایده بزنیم و بگیم خب اوکی این tuple مربوط به گزینه‌ها رو می‌بریم توی یه فایل دیگه تعریف می‌کنیم و این جا ایمپورتش می‌کنیم که کدمون تمیز بشه. این روش شاید در نگاه اوّل خیلی خوب و منطقی به نظر بیاد، ولی اگر یه روز ادمین سایت تصمیم بگیره که یه تغییری توی لیست دانشگاه‌ها اعمال کنه، باید بره کد منبع سایت رو ادیت کنه. پس فکر نمی‌کنم این هم روش جالبی باشه!توی این شرایط اوّلین راهی که به ذهنمون می‌رسه احتمالا اضافه کردن یک مدل «دانشگاه» به پایگاه داده‌مون هست. این طوری می‌تونیم یه لیست از دانشگاه‌ها رو به عنوان instanceهای این مدل ذخیره کنیم و فیلد university که توی مدل «دانشجو» داشتیم، تبدیل به یک کلید خارجی به اون مدل بشه. یعنی در عمل همچین کدی داشته باشیم:class University(models.Model):
    name = models.CharField(max_length=100, unique=True)

class Student(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    university = models.ForeignKey(University, on_delete=models.SET_NULL, null=True)تا این جای کد به نظر خیلی خوب بوده. امّا تصوّر کنید می‌خوایم بریم سراغ serialize کردن Student. بدیهی‌ترین روش اینه که یه سریالایزر ساده بسازیم.class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = [&#039;first_name&#039;, &#039;last_name&#039;, &#039;university&#039;]این روش به عنوان university، آیدی دانشگاه رو برمی‌گردونه نه اسم اون رو. این امر بعضی جاها خوبه! امّا بعضی جاها هم این برای ما مطلوب نیست. یک راه دم دستی که می‌تونه این جا استفاده بشه، اینه که توی پایگاه داده‌مون، Primary Key این مدل رو روی فیلد name ست کنیم.ولی باز هم ممکنه که بعضی جاها این کار مطلوب نباشه. یه روش دیگه هم که می‌شه بهش فکر کرد اینه که همچین کدی داشته باشیم برای سریالایزمون:class StudentSerializer(serializers.ModelSerializer):
    university = serializers.CharField(source=&#039;university.name&#039;)

    class Meta:
        model = Student
        fields = [&#039;first_name&#039;, &#039;last_name&#039;, &#039;university&#039;]امّا توی این روش موقع اجرای متد save روی سریالایزرمون ممکنه به خاطر وجود &#x60;.&#x60; توی منبع فیلد university به مشکلی بخوریم.چندین و چند روش دیگه هم می‌شه ارائه داد برای حل این مشکلات. یکی از تمیزترین راه‌هایی که دیدم این هست که با متدهای to_internal_value و to_representation بازی کنیم!در مورد این مثال خاص دانشگاه، می‌شه چنین کاری کرد:class UniversityField(fields.Field):
    default_error_messages = {
        &#039;invalid&#039;: _(&#039;Not a valid university.&#039;),
        &#039;nonstring&#039;: _(&#039;Not a string.&#039;),
    }

    def run_validation(self, data=empty):
        if data is not None and not isinstance(data, str):
            self.fail(&#039;nonstring&#039;)
        return super().run_validation(data)

    def to_internal_value(self, data):
        u = University.objects.filter(name=data)
        if u.count() != 1:
            self.fail(&#039;invalid&#039;)
        return u.get()

    def to_representation(self, value):
        return value.nameیعنی یک فیلد می‌سازیم که قرار هست مدل دانشگاه رو برامون سریالایز کنه. این جا ما از Field ارث می‌بریم چون دانشگاه فقط و فقط یک مشخّصه‌ی &#x60;name&#x60; داره. اگر تا حالا این کار رو نکردید، پنیک نکنید! ارث بردن از Field هم مثل ارث‌بری از Serializer و ModelSerializer هست. اگر سورس کد فریم‌ورک جنگو رست رو بخونیم، می‌بینیم که ModelSerializer هم به طور غیرمستقیم از Field ارث‌بری می‌کنه حتّی!حالا سریالایزر Student رو به این شکل بازنویسی می‌کنیم:class StudentSerializer(serializers.ModelSerializer):
    university = UniversityField()

    class Meta:
        model = Student
        fields = [&#039;first_name&#039;, &#039;last_name&#039;, &#039;university&#039;]این روش رو از این نظر می‌پسندم(!) که متدهای to_representation، to_internal_value و run_validation به طور اتوماتیک توسّط باقی متدها در حین validate کردن و serialize کردن و deserialize کردن و... صدا زده می‌شن و لازم نیست ما جایی کد اضافه‌ای بزنیم.البته دقّت کنید که چون ما فیلد دانشگاه رو می‌تونستیم با یه رشته مشخّص کنیم، برای سریالایز کردنش از کلاس Field ارث‌بری کردیم. فرض کنید فیلد چندگزینه‌ای‌مون، محل زندگی بود: یک شیء با ۳ فیلد شهر، استان و کشور. در این حالت باید برای سریالایز کردن محل زندگی، سریالایزری بسازیم که از ModelSerializer و یا Serializer ارث‌بری کنه.این مطلب همین جا تموم می‌شه! ولی اگر روش تمیزی برای حل چنین مسائلی داشتید، خوش‌حال می‌شم توی کامنت‌ها بگید.خوش بگذره! ;)</description>
                <category>راکب</category>
                <author>راکب</author>
                <pubDate>Thu, 02 Apr 2020 13:57:06 +0430</pubDate>
            </item>
            </channel>
</rss>