2010-08-20 6 views
8

Actualmente estoy trabajando en un sistema de informe de errores basado en excepciones para aplicaciones Windows MSVC++ (9.0) (es decir, estructuras de excepción & tipos/herencia, pila de llamadas, registro de errores &, etc.).

Mi pregunta ahora es: ¿cómo informar correctamente & registrar un error de memoria?

Cuando se produce este error, p. como bad_alloc lanzada por el new op, puede haber muchas "características" no disponibles, principalmente relacionadas con la asignación de memoria adicional. Normalmente, apruebo la excepción a la aplicación si se ha lanzado en una lib y luego uso los cuadros de mensaje y los archivos de registro de errores para informar y registrarla. Otra forma (principalmente para los servicios) es usar el registro de eventos de Windows.
El principal problema que tengo es armar un mensaje de error. Para proporcionar cierta información de error, me gustaría definir un mensaje de error estático (puede ser una cadena literal, mejor una entrada en un archivo de mensaje, luego usar FormatMessage) e incluir alguna información en tiempo de ejecución como una pila de llamadas.
Las funciones/métodos necesarios para este uso ya seaC++/Windows: cómo informar una excepción de falta de memoria (bad_alloc)?

  • STL (std::string, std::stringstream, std::ofstream)
  • CRT (swprintf_s, fwrite)
  • o Win32 API (StackWalk64, MessageBox, FormatMessage, ReportEvent, WriteFile)

Además de ser documentada en la MSDN, todos ellos más (Win32) o menos (STL) de fuente cerrada en Windows, por lo que no sé realmente cómo se comportan en problemas de poca memoria.

Sólo para demostrar que puede haber problemas, escribí una pequeña aplicación trivial provocando una bad_alloc:

int main() 
{ 
    InitErrorReporter(); 

    try 
    { 
     for(int i = 0; i < 0xFFFFFFFF; i++) 
     { 
      for(int j = 0; j < 0xFFFFFFFF; j++) 
      { 
       char* p = new char; 
      } 
     } 
    }catch(bad_alloc& e_b) 
    { 
     ReportError(e_b); 
    } 

    DeinitErrorReporter(); 

    return 0; 
} 

Ran dos instancias w/o depurador asociado (en la configuración de lanzamiento, VS 2008), pero "no pasó nada ", es decir, no hay códigos de error de ReportEvent o WriteFile que utilicé internamente en el informe de errores. Luego, lanzó una instancia con y sin depurador y les permitió tratar de informar sus errores uno tras otro utilizando un punto de interrupción en la línea ReportError. Eso funcionó bien para la instancia con el depurador conectado (informó correctamente & registró el error, incluso utilizando LocalAlloc sin problemas)! Pero el taskman mostró un comportamiento extraño, donde hay mucha memoria liberada antes de que la aplicación salga, supongo que cuando se lanza la excepción.


Por favor, considere que puede haber más de un proceso [editar] y más de un hilo [/ editar] consumir mucha memoria, así liberando espacio de montón pre-asignado no es una solución segura para evitar un entorno de memoria baja para el proceso que quiere reportar el error.

Gracias de antemano!

+2

¿Ha considerado tener un bloque de memoria reservado de antemano que se convertirá en la fuente de colocación asignaciones cada vez que el sistema entra en una situación de falta de memoria? Solo he usado este método para salir con gracia de la aplicación, pero los sistemas operativos (OpenSolaris, Linux) hacen algo similar para dar a la aplicación el tiempo suficiente para liberar o intercambiar asignaciones de baja prioridad y recuperar con gracia. – Sniggerfardimungus

+0

Actualmente, Uso un poco de espacio en la pila (las variables miembro declaradas al llamar a InitErrorReporter) para proporcionar búferes a las funciones CRT/WinSDK. Pero no sé lo que hacen internamente: ver el informe de Alex Farber. – dyp

+0

http://stackoverflow.com/questions/1308052/policy-with-catching-stdbad-alloc hablar sobre algo similar – Chubsdad

Respuesta

2
  • pre-asignar el almacenamiento intermedio (s) que necesita
  • enlace estática y utilizar _beginthreadex en lugar de CreateThread (de lo contrario, las funciones CRT pueden fallar) - O - aplicar la cadena concat/I2A mismo
  • uso de mensaje (MB_SYSTEMMODAL | MB_OK) MSDN menciona esto para reportar condiciones OOM (y algunos EM blogger describe este comportamiento según lo previsto: el cuadro de mensaje no va a asignar memoria.)

tala es más difícil, por lo menos, la el archivo de registro debe estar abierto ya.

Probablemente lo mejor con FILE_FLAG_NO_BUFFERING y FILE_FLAG_WRITE_THROUGH, para evitar cualquier intento de almacenamiento en el búfer. El primero requiere que las escrituras y los almacenamientos intermedios de memoria estén alineados con el sector (es decir, debe consultar GetDiskFreeSpace, alinear el almacenamiento intermedio con eso y escribir solo en desplazamientos de archivos "múltiples de tamaño de sector" y en bloques que son múltiplos de tamaño de sector. No estoy seguro de si esto es necesario o útil, pero un OOM en todo el sistema donde cada asignación falla es difícil de simular.

+1

muchas gracias. Hice algunas pruebas (sin embargo, no sé cuán significantes son) y el MessageBox | MB_SYSTEMMODAL siempre apareció, incluso después de atrapar varios bad_allocs. Para esto, utilicé un código basado en el de mi propia respuesta, pero decidí construirlo para x64/Release. Los bad_allocs que recibí con x86 fueron en su mayoría errores de espacio de direcciones virtuales, ya que tengo 4 GB de RAM más (ahora) el tamaño máximo de archivo de página de 2 GB. Con la versión x64, el sistema realmente se cuelga severamente, incluso el cursor algunas veces. ¡Pero aún así, MB funciona y ese truco con la página que mencioné también! – dyp

3

"Liberar el espacio de almacenamiento preasignado ...". Esto fue exactamente lo que pensé al leer tu pregunta. Pero creo que puedes intentarlo. Cada proceso tiene su propio espacio de memoria virtual. Con otros procesos que consumen mucha memoria, esto aún puede funcionar si toda la computadora está funcionando.

+0

k, pero se suponía que había dos subprocesos en una aplicación donde ambos ejecutaban funciones de bibliotecas diferentes. Un proceso -> un espacio de memoria virtual. Entonces, ¿cuándo uno arroja un bad_alloc y luego libera el espacio preasignado, el otro podría asignarlo? El problema es que cuando uso las funciones de OS/SDK, no sé si confían internamente en el espacio de la cabeza, así que usar el espacio preasignado para operar no era la solución que yo creía. – dyp

+0

tengo que suspender todos los otros hilos antes de que pueda usar esta técnica? – dyp

+0

Otros hilos pueden usar inmediatamente la memoria preasignada que acaba de lanzarse ... Cada actividad debe detenerse para asegurarse de que el informe de error tenga éxito. –

1

Considere que puede haber más de un proceso consumiendo mucha memoria, por lo que liberar espacio de montón preasignado no es una solución segura para evitar un entorno de poca memoria para el proceso que desea informar el error.

En Windows (y otros sistemas operativos modernos), cada proceso tiene su propio espacio de direcciones (también conocido como memoria) separado de cualquier otro proceso en ejecución. Y todo eso está separado de la RAM literal en la máquina. El sistema operativo ha virtualizado el espacio de direcciones de proceso lejos de la RAM física.

Así es como Windows puede enviar la memoria utilizada por los procesos al archivo de página en el disco duro sin que esos procesos tengan conocimiento de lo sucedido.

También es así como un solo proceso puede asignar más memoria que la máquina tiene RAM física y todavía se ejecuta. Por ejemplo, un programa que se ejecute en una máquina con 512 MB de RAM aún podría asignar 1 GB de memoria. Windows simplemente no podría mantener todo en la memoria RAM al mismo tiempo y parte de eso estaría en el archivo de la página. Pero el programa no lo sabría.

Por lo tanto, si un proceso asigna memoria, no causa que otro proceso tenga menos memoria para trabajar. Cada proceso es separado.

Cada proceso solo tiene que preocuparse por sí mismo. Y entonces la idea de liberar un trozo de memoria preasignado es realmente muy viable.

+0

Estoy de acuerdo. ¿Qué hay sobre dos hilos? Ver la respuesta de Alex Farber. – dyp

+0

¿Eh? Esto no es correcto. Si bien cada proceso tiene su propio espacio VA, hay un número limitado de marcos físicos y espacio de intercambio para satisfacer una solicitud de entrada de página. Si el cargo de compromiso total supera este número, las solicitudes de asignación de cada proceso fallarán. Si te metes en esta situación, liberar un trozo de memoria significa que cualquier proceso en el sistema puede tomarlo (es decir, cualquier proceso podrá volver a aumentar la carga de confirmación). –

+0

@Paul: Usted tiene un punto. No importa lo que haga el sistema operativo, siempre habrá un límite eventualmente. Sin embargo, fuera de situaciones en las que los mecanismos de memoria del sistema operativo se han agotado, me pregunto si no es razonable que los programas actúen como si siempre tuvieran su espacio de direcciones virtuales completo para trabajar. Pero supongo que puede ser específico de la situación, según la probabilidad de que la máquina del usuario final maximice sus recursos y cuán útil sería iniciar sesión en respuesta a dicha situación. – TheUndeadFish

0

No puede usar las funciones CRT o MessageBox para manejar OOM ya que pueden necesitar memoria, como usted describe. La única cosa verdaderamente segura que puede hacer es asignar un trozo de memoria al inicio, puede escribir información y abrir un identificador en un archivo o una tubería, y luego escribir en WriteFile cuando salga.

+0

Lo que quiere decir es que, en lugar de preasignar y luego liberar un trozo, en su lugar, preasignaría un área que se usaría específicamente para manejar los datos grabados en el escenario OOM (una especie de búfer de raspado para ensamblar el cuerdas o lo que sea deseado). Suponiendo que WriteFile nunca tiene una razón para fallar en un escenario OOM, entonces tendría que aceptar que esto suena como la mejor forma garantizada de registrar la información. – TheUndeadFish

+0

Sí, esto es lo que hace el sistema operativo si debe garantizar que algo funciona en condiciones de falta de memoria (como ser capaz de generar una excepción SEH, ntdll siempre mantiene uno preasignado en su bolsillo trasero) –

+0

¿Sabe cómo? creación de los diferentes tipos de objetos Win32 se comporta en condiciones OOM? Es decir. Objetos Kernel como archivos (-> manejadores de archivos) y objetos de GUI como MessageBoxes? Por lo que yo sé, al menos los objetos Kernel no son parte de su espacio de dirección/memoria virtual y, por lo tanto, el sistema operativo realmente les asigna memoria. – dyp

Cuestiones relacionadas