[Powered by Google Translate] [Valgrind] [נייט Hardison, אוניברסיטת הרווארד] זה CS50, CS50.TV] חלק מהבאגים הקשים ביותר בתוכניות C באו מהניהול הכושל של זיכרון. יש מספר עצום של דרכים לתפשל, כולל הקצאת הכמות הנכונה של זיכרון, שוכח לאתחל משתנה, כתיבה לפני או אחרי סוף החיץ, ולשחרר לשמור זמני זיכרון מרובים. הסימפטומים נעים בין קריסות לסירוגין לערכים מסתוריים נדרסו, לעתים קרובות במקומות ובזמנים רחוקים מאוד מטעות המקורית. לאתר את הבעיה שנצפתה בחזרה לשורש הבסיסי יכול להיות מאתגר, אבל למרבה המזל, יש תכנית בשם מועילה Valgrind שיכול לעשות הרבה כדי לעזור. אתה מפעיל תכנית תחת Valgrind כדי לאפשר בדיקה מקיפה של הקצאות ומקבלת גישה לזכרון ערימה. כאשר Valgrind מזהה בעיה, זה נותן לך באופן מיידי, מידע ישיר שמאפשר לך בקלות רבה יותר לאתר ולפתור את הבעיה. Valgrind גם דיווחים על בעיות זיכרון פחות קטלניות, כגון דליפות זיכרון, הקצאת זכרון ערימה, ושוכח לשחרר אותו. רוצה המהדר, קלאנג, בבאגים שלנו, GDB, Valgrind הוא תוכנה חופשיה, והוא מותקן על המכשיר. Valgrind פועל על ההפעלה בינארי שלך, לא. קבצי קוד מקור h. ג שלך או, כדי להיות בטוח שיש לך הידור עותק עד למועד התכנית שלך באמצעות קלאנג או הצע. ואז, להפעיל את התכנית תחת Valgrind יכול להיות פשוט כמו סתם קידומת פקודת התכנית הסטנדרטית עם המילה Valgrind, אשר מתחיל את Valgrind ומריץ את התוכנה הפנימית שלו. כאשר מתחילים, Valgrind עושה קצת מורכב הניע להגדיר הפעלה עבור בדיקות הזיכרון, כך שזה יכול לקחת קצת לקום ולפעול. התכנית לאחר מכן לבצע בדרך כלל, תהיה זה הרבה יותר לאט, וכאשר היא מסתיימת, Valgrind יודפס סיכום של השימוש בזיכרון שלו. אם הכל ילך כשורה, זה ייראה בערך כך: במקרה זה,. / Clean_program היא הדרך לתכנית אני רוצה להריץ. ובזמן שזה לא לוקח כל ויכוחים אם זה לא הייתי רק טקטיקתם לסוף הפקודה כרגיל. תכנית נקיה היא רק תכנית קטנה וטיפשית שנוצרתי שמקצה מקום לבלוק של ints בערמה, לשים כמה ערכים בתוכם, ומשחרר את כל אגף. זה מה שאתה יורה ל, ללא שגיאות וללא הדלפות. מדד חשוב נוסף הוא המספר הכולל של בתים שהוקצו. בהתאם לתכנית, אם ההקצאות שלך הן במגה ומעלה, אתה כנראה עושה משהו לא בסדר. האם אתה צורך אחסון עותקים? האם אתה משתמש בערימה לאחסון, כאשר זה יהיה יותר טוב להשתמש במחסנית? אז, שגיאות זיכרון יכולות להיות באמת רעות. אלה גלויים יותר לגרום לקריסות מרהיבות, אבל גם אז הוא עדיין יכול להיות קשה לשים את האצבע מה בדיוק הוביל להתרסקות. יותר חתרנית, תכנית עם שגיאת זיכרון עדיין יכול לקמפל נקי ויכול עדיין נראה לעבוד כראוי בגלל שהצלחת לקבל מזל רוב הזמן. לאחר כמה "תוצאות מוצלחות", אתה יכול פשוט חושב שהתרסקות היא מקרית בהחלט של המחשב, אבל המחשב לא טועה אף פעם. ריצת Valgrind יכולה לעזור לך לאתר את הגורם לשגיאות זיכרון גלויות כמו גם למצוא אורבים טעויות אתה אפילו לא יודע עדיין עליו. בכל פעם Valgrind מזהה בעיה, הוא מדפיס את מידע על מה שנצפה. כל פריט הוא די תמציתי - שורת המקור של ההוראה הפוגע, מה הבעיה היא, וקצת מידע על הזיכרון המעורב - אבל לעתים קרובות זה מספיק מידע כדי להפנות את תשומת לבך למקום הנכון. הנה דוגמה של Valgrind פועלת על תכנית מרכבה שעושה קריאה לא חוקית של זכרון ערימה. אנחנו לא רואים שום שגיאות או אזהרות בהידור. אוי, סיכום השגיאה אומר שיש שתי טעויות - 2 כניסות לא חוקיות בגודל 4 - בתים, כי הוא. שניהם הרעים קורא התרחש בפונקציה העיקרית של invalid_read.c, הראשון על קו 16 והשני בקו 19. בואו נסתכל על הקוד. נראה כמו השיחה הראשונה printf למנסה לקרוא int 1 עבר סוף בלוק הזיכרון שלנו. אם אנחנו מסתכלים אחורה בתפוקה של Valgrind, אנו רואים שValgrind אמר לנו בדיוק את זה. הכתובת שאנחנו מנסים לקרוא מתחילה 0 בתים מעבר לקצה הבלוק בגודל 16 בתים - 4 ints 32-bit שהוקצינו. כלומר, הכתובת היינו מנסה לקרוא מתחילה ממש בסוף של הבלוק שלנו, בדיוק כפי שאנו רואים בשיחת printf הרעה שלנו. עכשיו, לא חוקי כניסות אולי לא נראות כמו כל כך גדול של עסקה, אבל אם אתה משתמש בנתונים אלה כדי לשלוט בזרימה של התכנית שלך - לדוגמה, כחלק מאם הצהרה או לולאה - אז דברים בשקט יכולים להתקלקל. תראה איך אני יכול להפעיל את תכנית invalid_read ושום דבר יוצא דופן קורה. מפחיד, הא? עכשיו, בואו נסתכל על עוד כמה סוגים של טעויות שאתה עשוי להיתקל בקוד שלך, ואנחנו נראה איך Valgrind מזהה אותם. פשוט ראינו את דוגמה של invalid_read, אז עכשיו בואו נבדוק את invalid_write. שוב, ללא שגיאות או אזהרות בהידור. אוקיי, Valgrind אומר שיש שתי שגיאות בתכנית זו - וinvalid_write וinvalid_read. בואו לבדוק את הקוד הזה. נראה שיש לנו דוגמה של strlen הקלסיים בתוספת אג אחד. הקוד לא malloc ייט של שטח נוסף ל/ 0 אופי, לכן כאשר str עותק הלך לכתוב את זה בssubstrlen "cs50 סלעים!" זה כתב בית 1 עבר סוף הבלוק שלנו. Invalid_read מגיע כאשר אנו עושים קריאתנו לprintf. Printf מסתיים בקריאה לא חוקי לזיכרון כאשר הוא קורא את התו / 0 כמו שזה נראה בסופו של המייתר הזה זה דפוס. אבל כל זה לא נמלט Valgrind. אנו רואים שזה תפס את invalid_write כחלק מעותק str על קו 11 של ראשי, וinvalid_read הוא חלק מprintf. רוק ב, Valgrind. שוב, זה אולי לא נראה כמו עניין גדול. אנחנו יכולים להפעיל את התכנית שוב ושוב מחוץ לValgrind ולא רואה שום סימפטומי שגיאה. עם זאת, בואו נסתכל על וריאציה קלה של זה כדי לראות איך דברים יכולים להיות ממש לא רעים. לכן, מובן מאליו, אנחנו מנצלים לרעה את הדברים יותר מאשר רק קצת בקוד זה. אנחנו רק הקצאת שטח על הערמה במשך שתי מחרוזות אורך cs50 סלעים, הפעם, נזכר בדמות / 0. אבל אז אנחנו זורקים במחרוזת ארוכה במיוחד לבלוק הזיכרון שS כדי להצביע. איזו השפעה תהיה לכך על בלוק הזיכרון שנקודתי T ל? ובכן, אם נקודתי T לזיכרון, שרק בסמוך לS, מגיע רק אחרי זה, אז אולי כתבתי על חלק ט בואו להפעיל את הקוד הזה. תראה מה קרה. מייתרינו מאוחסנים בערמתנו ששני נראה היה שהודפסו בצורה נכונה. שום דבר לא נראה רע בכלל. עם זאת, בואו נחזור לקוד שלנו ו הערה את הקו שבו אנחנו להעתיק cs50 סלעים לבלוק הזיכרון השני, הצביע על ידי לא. כעת, כאשר אנו מפעילים את הקוד הזה אנחנו צריכים רואה רק את התוכן של בלוק הזיכרון הראשון להדפיס. וואו, למרות שלא עשו str עותק תווים כלשהם לתוך בלוק הערימה השנייה, זו שהצביעו על ידי T, אנחנו מקבלים את הדפסה. ואכן, את המחרוזת אנחנו דחוסים לתוך הבלוק הראשון שלנו כבש את הבלוק הראשון ולבלוק השני, עושה הכל נראה נורמלי. Valgrind, אם כי, מספר לנו את הסיפור האמיתי. הנה. כל אלה לא חוקיים קורא וכותב. בואו נסתכל על דוגמה של סוג אחר של שגיאה. הנה אנחנו עושים משהו מאוד לא נעים. אנו תופסים את המרחב לint בערמה, ולאתחל מצביע int - עמ '- כדי להצביע על מקום זה. עם זאת, בעוד שהמצביע שלנו מאותחל, את הנתונים שהוא מצביע על מה שיש רק זבל הוא שבחלק מהערימה. לכן, כאשר אנו לטעון את הנתונים לתוך int i, אנחנו מבחינה טכנית אני לאתחל, אבל אנו עושים זאת עם נתוני זבל. השיחה כדי לטעון, שהוא מאקרו ניפוי שימושי הגדרתו בספרייה בשם צדק לטעון, תסגר התכנית אם תנאי המבחן שלה נכשל. כלומר, אם אני לא 0. תלוי מה היה בשטח הגל, שאליו מצביע p, תכנית זו יכולה לעבוד לפעמים ונכשלת בזמנים אחרים. אם זה עובד, אנחנו פשוט מקבלים מזל. המהדר לא יתפוס את השגיאה, אבל Valgrind בטוח יהיו. יש לנו לראות את השגיאות הנובעות מהשימוש בנתונים אלה הזבל שלנו. כשאתה להקצות זכרון ערימה אבל לא deallocate או לשחרר אותו, זה נקרא דליפה. לתכנית קטנה, קצרת מועד שפועלת ומייד עם יציאתו הדלפות הן תמימות למדי, אך לפרויקט בגודל גדול יותר ו / או אריכות ימים, אפילו דליפה קטנה יכולה מתחמת למשהו גדול. לCS50, אנחנו מצפים ממך לדאוג לשחרור כל זכרון הערימה שתקצה, מכיוון שאנחנו רוצים אותך כדי לבנות את הכישורים לטפל כראוי התהליך הידני הנדרש על ידי ג כדי לעשות זאת, התכנית שלך צריכה להיות מדויקת התאמה של אחד לאחד בין malloc ושיחות חינם. למרבה המזל, Valgrind יכול לעזור לך עם דליפות זיכרון מדי. הנה תכנית דולפת נקראת leak.c שמקצה מקום בערמה, כותב אליו, אבל לא ישחרר אותו. אנו עורכים אותו עם הפכו ולהפעיל אותו תחת Valgrind, ואנחנו רואים את זה, בזמן שאין לנו שגיאות זיכרון, יש לנו דליפה אחת. יש 16 בתים בהחלט איבוד, כלומר את המצביע לזיכרון שלא היה בהיקפה כאשר התכנית יצאה. עכשיו, Valgrind לא נותן לנו המון מידע על הדליפה, אבל אם תעקוב אחרי הפתק הקטן הזה שזה נותן לכיוון החלק התחתון של הדו"ח שלה לשידור חוזר עם - דליפה לבדוק = מלא כדי לראות את הפרטים המלאים של זיכרון שדלף, נקבל מידע נוסף. כעת, בסיכום הערמה, Valgrind אומר לנו איפה הזיכרון שאבד בתחילה הוקצה. בדיוק כפי שאנו מכירים מהתבוננות בקוד המקור, Valgrind מודיע לנו שאנחנו הדלפנו את הזיכרון הוקצה בקריאה לmalloc על קו 8 של leak.c בתפקיד הראשי. די מגניב. Valgrind מסווג הדלפות שימוש במונחים אלה: אבדתי בהחלט - זה זיכרון שהוקצה ערימה כדי שהתכנית כבר לא מצביעה. Valgrind יודע שפעם היה המצביע, אך מאז אבדת אותו. זיכרון זה בהחלט דלף. אבד בעקיפין - זה זיכרון שהוקצה ערימה כדי שהמצביעים רק לזה גם הולכים לאיבוד. לדוגמה, אם אבדת את המצביע לצומת הראשונה של רשימה מקושרת, אז הצומת הראשונה עצמו הייתה בהחלט הפסיד, בעוד שכל צומת באים יהיו אבודים באופן עקיף. ייתכן שאבדה - זה זיכרון שהוקצה ערימה כדי שValgrind לא יכול להיות בטוח אם יש מצביע או לא. עדיין נגיש הוא זיכרון שהוקצה ערימה כדי שהתכנית עדיין יש מצביע ביציאה, אשר בדרך כלל אומר שנקודות משתנות גלובליות לזה. כדי לבדוק אם קיים הדליפה האלה, אתה גם צריך לכלול את האפשרות - עדיין נגיש = כן בפנייה לValgrind. מקרים שונים אלה עלולים לחייב אסטרטגיות שונות לניקוים, אבל הדלפות יש לחסל. למרבה הצער, תיקון דליפות יכול להיות קשה לעשות, מאז שיחות שגויות לחופשיים יכולות לפוצץ את התכנית שלך. לדוגמה, אם אנו מסתכלים על invalid_free.c, אנו רואים דוגמה לdeallocation זיכרון הרע. מה צריך להיות שיחה אחת לשחרר את כל הגוש של זיכרון אליו מצביע int_block, הפך במקום ניסיון לשחרר את כל קטע int בגודל של הזיכרון בנפרד. זה ייכשל קטסטרופלי. בום! מה שגיאה. זה בהחלט לא טוב. אם אתה תקוע עם סוג זה של שגיאה, אם כי, ואתה לא יודע איפה לחפש, לנפול על גב החבר הכי הטוב החדש שלך. נחשת נכון - Valgrind. Valgrind, כמו תמיד, יודע בדיוק מה קורה. ספירת alloc וחופשיה אינה מתאימה. יש לנו alloc 1 ו 4 משחררים. וValgrind גם אומר לנו איפה שיחה חינם הרע הראשונה - אחד שהפעיל את הפיצוץ - שממנו מגיע - קו 16. כפי שאתם רואים, שיחות רעות לחינם הן באמת רעות, לכן מומלץ לתת דליפת התכנית שלך בזמן שאתה עובד על מקבל פונקציונלי נכון. להתחיל לחפש דליפות רק לאחר התכנית שלך אינה פועלת כראוי, ללא שגיאות אחרות. וזה כל מה שיש לנו בסרטון זה. עכשיו מה אתם מחכים? ללכת לנהל Valgrind על התוכניות שלך עכשיו. השם שלי הוא נייט Hardison. זה CS50. [CS50.TV]