اختبار التطبيقات في ظروف الذاكرة المنخفضة

إن مسألة ما إذا كان من الضروري التحقق من المردود mallocأمر مثير للجدل ويولد دائمًا نقاشًا ساخنًا.



يعتقد بعض الناس أننا يجب أن نحاول معالجة جميع أنواع أخطاء وقت التشغيل ، بما في ذلك. و OOM المواقف. يعتقد البعض الآخر أنه لا يزال هناك القليل مما يمكن عمله مع OOM ومن الأفضل ترك التطبيق يتعطل. على جانب المجموعة الثانية من الأشخاص ، هناك أيضًا حقيقة أنه من الصعب اختبار منطق معالجة OOM الإضافي. وإذا لم يتم اختبار الكود ، فمن المؤكد أنه لا يعمل.



أوافق تمامًا على أنه لا يجب عليك تنفيذ منطق معالجة الأخطاء الذي لن تختبره. يكاد يكون من المؤكد أنها لن تحسن أي شيء ، وربما أسوأ - تدمير كل شيء.



إن مسألة ما إذا كان يجب محاولة التعامل مع مواقف OOM في المكتبات / التطبيقات أمر مثير للجدل ولن نتطرق إليه هنا. كجزء من هذا المنشور ، أريد فقط أن أشارك تجربتي حول كيفية اختبار المنطق المطبق للتعامل مع مواقف OOM في التطبيقات المكتوبة بلغة C / C ++. ستكون المحادثة حول أنظمة تشغيل Linux و macOS. لعدد من الأسباب ، سيتم تجاوز Windows.



المقدمة



نتمنى جميعًا ألا تحدث OOM أبدًا ، ولكن في الحياة الواقعية لا يكون هذا ممكنًا دائمًا للأسباب التالية:



  • ذاكرة الوصول العشوائي محدودة دائمًا.
  • لا يتم تمكين SWAP دائمًا.
  • لا تتصرف التطبيقات دائمًا بشكل كافٍ وتحاول أحيانًا تخصيص كميات كبيرة غير واقعية من الذاكرة ، وتتداخل مع نفسها ومع الآخرين.
  • لا تزال تطبيقات 32 بت موجودة.
  • لا يتم تمكين الإفراط في الالتزام دائمًا.
  • يمكن تقييد استهلاك الذاكرة باستخدام ulimit، على سبيل المثال.
  • من خلال LD_PRELOADالتطبيق نفسه ، يمكن إصدار مخصص معين ، والذي قد لا يصدر ذاكرة تتجاوز الحد المعين.


إذا كنت قد قرأت هذا الآن ، فسوف نفترض أنك توافق على أن التعامل مع OOM مهم وضروري. لماذا هذا مهم لنفسه ، الجميع يقرر بنفسه ، من جانبي يمكنني القول بإيجاز أن المتطلبات التالية قد تم فرضها على العديد من المكونات التي عملت عليها:



  • يجب ألا يتعطل المكون أبدًا ، ويجب ألا تتعطل التطبيقات التي تستخدمه بسبب أخطاء في هذه المكونات.
  • OOM . .
  • . , .


, , SQLite. , . . SQLite .



, , , . OOM Killer, , . , C++, .



1.



, OOM . my_malloc my_free malloc free.



my_free . my_realloc.



my_malloc malloc . my_malloc , NULL .



, :



  • 3rd party .
  • malloc . - strdup.
  • malloc’ , , .
  • C++ malloc free.


- .



2.



Linux LD_PRELOAD. . malloc. , malloc/realloc/free (weak). , macOS LD_PRELOAD, DYLD_INSERT_LIBRARIES.



, , LD_PRELOAD DYLD_INSERT_LIBRARIES malloc/realloc NULL .



, "" . , .



, "" , , . :



  • main. , .
  • Runtime macOS " ". , , , .
  • printf macOS SIGSEGV/SIGBUS.
  • , std::bad_alloc, . , , , OOM. std::terminate. .
  • std::thread std::terminate macOS.


UPDATE: Travis CI , macOS / Xcode , std::bad_alloc , std::thread std::terminate.



Overthrower. - malloc NULL. Overthrower - , .



3.



main



, main , main runtime . main, , .. - main.



, main . Overthrower, OOM . Overthrower , .



:



  • activateOverthrower
  • deactivateOverthrower


:



#ifdef __cplusplus
extern "C" {
#endif
void activateOverthrower() __attribute__((weak));
unsigned int deactivateOverthrower() __attribute__((weak));
#ifdef __cplusplus
}
#endif


.



Overthrower LD_PRELOAD, NULL, , .



, , :



int main(int argc, char** argv)
{
    activateOverthrower();
    // Some code we want to test ...
    deactivateOverthrower();
}


activateOverthrower/deactivateOverthrower , :



TEST(Foo, Bar)
{
    activateOverthrower();
    // Some code we want to test ...
    deactivateOverthrower();
}


, -, , Overthrower , :



#ifdef __cplusplus
extern "C" {
#endif
void pauseOverthrower(unsigned int duration) __attribute__((weak));
void resumeOverthrower() __attribute__((weak));
#ifdef __cplusplus
}
#endif


:



TEST(Foo, Bar)
{
    activateOverthrower();
    // Some code we want to test ...
    pauseOverthrower(0);
    // Some fragile code we can not fix ...
    resumeOverthrower();
    // Some code we want to test ...
    deactivateOverthrower();
}


Overthrower .





__cxa_allocate_exception, , , malloc, NULL. , Linux, malloc, __cxa_allocate_exception (emergency buffer), , . .



macOS , , , , std::bad_alloc, std::terminate.



UPDATE: , macOS / Xcode .



, , , __cxa_allocate_exception malloc. - Overthrower’ malloc. Overthrower malloc __cxa_allocate_exception.



, , , macOS __cxa_atexit, Linux dlerror. .



Overthrower , malloc free. Overthrower’ , activateOverthrower deactivateOverthrower , :



overthrower got deactivation signal.
overthrower will not fail allocations anymore.
overthrower has detected not freed memory blocks with following addresses:
0x0000000000dd1e70  -       2  -         128
0x0000000000dd1de0  -       1  -         128
0x0000000000dd1030  -       0  -         128
^^^^^^^^^^^^^^^^^^  |  ^^^^^^  |  ^^^^^^^^^^
      pointer       |  malloc  |  block size
                    |invocation|
                    |  number  |


Overthrower , , .



Overthrower’, , valgrind. , OOM. , , Overthrower . Overthrower , , deactivateOverthrower , stderr .





Overthrower 3 :



  • Random — rand() % duty_cycle == 0. duty_cycle, .
  • Step — (malloc_seq_num >= delay), delay .


<--- delay --->
--------------+
              |
              | All further allocations fail
              |
              +------------------------------


  • Pulse — (malloc_seq_num > delay && malloc_seq_num <= delay + duration), delay duration .


<--- delay --->
--------------+                +------------------------------
              |                |
              |                | All further allocations pass
              |                |
              +----------------+
              <--- duration --->


:



  • OVERTHROWER_STRATEGY
  • OVERTHROWER_SEED
  • OVERTHROWER_DUTY_CYCLE
  • OVERTHROWER_DELAY
  • OVERTHROWER_DURATION


activateOverthrower. , Overthrower , /dev/urandom.



README.md.





  • Overthrower malloc /.
  • .
  • Overthrower .
  • Overthrower .
  • Overthrower , .
  • Overthrower’ .
  • Overthrower Overthrower-aware . , .
  • تم اختبار Overthrower نفسه على Ubuntu (منذ 14.04) و macOS (منذ Sierra (10.12) و Xcode 8.3). أثناء الاختبار ، يحاول Overthrower إسقاط نفسه ، من بين أمور أخرى.
  • إذا ظهرت OOM حقيقية في النظام ، فإن Overthrower يبذل قصارى جهده حتى لا يسقط نفسه.



All Articles