delphi - La corrupción de la memoria en System.Move debido al modo 8087CW cambiado(png+stretchblt)
x87 (3)
Tengo un extraño problema de corrupción de memoria. Después de muchas horas de depuración e intento, creo que encontré algo.
Por ejemplo: hago una simple asignación de cadenas:
sTest := ''SET LOCK_TIMEOUT '';
Sin embargo, el resultado a veces se convierte en:
sTest = ''SET LOCK''#0''TIMEOUT ''
Entonces, el _ es reemplazado por un byte 0.
He visto esto una vez (la reproducción es complicada, depende del tiempo) en la función System.Move, cuando usa la pila FPU (fild, fistp) para la copia de memoria rápida (en el caso de que se muevan de 9 a 32 bytes):
...
@@SmallMove: {9..32 Byte Move}
fild qword ptr [eax+ecx] {Load Last 8}
fild qword ptr [eax] {Load First 8}
cmp ecx, 8
jle @@Small16
fild qword ptr [eax+8] {Load Second 8}
cmp ecx, 16
jle @@Small24
fild qword ptr [eax+16] {Load Third 8}
fistp qword ptr [edx+16] {Save Third 8}
...
Usando la vista FPU y 2 vistas de depuración de memoria (Delphi -> Ver -> Depurar -> CPU -> Memoria) Vi que iba mal ... una vez ... no se pudo reproducir sin embargo ...
Esta mañana he leído algo sobre el modo 8087CW, y sí, si esto se cambia a $ 27FI, ¡se daña la memoria! Normalmente es $ 133F:
La diferencia entre $ 133F y $ 027F es que $ 027F configura la FPU para hacer cálculos menos precisos (limitando a Doble en vez de Extendido) y diferente manejo infiniti (que se usó para FPU más antiguas, pero ya no se usa).
De acuerdo, ahora encontré por qué, pero no cuando !
Cambié el funcionamiento de mi AsmProfiler con una simple comprobación (por lo que todas las funciones están marcadas en enter y leave):
if Get8087CW = $27F then //normally $1372?
if MainThreadID = GetCurrentThreadId then //only check mainthread
DebugBreak;
"Perfilé" algunas unidades y dll''s y bingo (ver pila):
Windows.StretchBlt(3372289943,0,0,514,345,4211154027,0,0,514,345,13369376)
pngimage.TPNGObject.DrawPartialTrans(4211154027,(0, 0, 514, 345, (0, 0), (514, 345)))
pngimage.TPNGObject.Draw($7FF62450,(0, 0, 514, 345, (0, 0), (514, 345)))
Graphics.TCanvas.StretchDraw((0, 0, 514, 345, (0, 0), (514, 345)),$7FECF3D0)
ExtCtrls.TImage.Paint
Controls.TGraphicControl.WMPaint((15, 4211154027, 0, 0))
Así que está sucediendo en StretchBlt ...
¿Qué hacer ahora? ¿Es un error de Windows o un error en PNG (incluido en D2007)? ¿O la función System.Move no es a prueba de fallas?
Nota: simplemente intentar reproducir no funciona:
Set8087CW($27F);
sSQL := ''SET LOCK_TIMEOUT '';
Parece ser más exótico ... Pero mediante la depuración en ''Get8087CW = $ 27F'' podría reproducirlo en otra cadena: FPU parte 1: FPU parte 2: FPU parte 3: Final de FPU: corrupto !:
Nota 2: Tal vez la pila FPU debe borrarse en System.Move?
No he visto este problema en particular, pero Move definitivamente se puede arruinar si la FPU está en mal estado. El controlador VPN de Cisco puede estropear las cosas horriblemente, incluso si no está haciendo nada relacionado con la red.
http://brianorr.blogspot.com/2006/11/intel-pentium-d-floating-point-unit.html
http://www.dankohn.com/archives/343
http://blog.excastle.com/2007/08/28/delphi-bug-of-the-day-fpu-stack-leak/ (comentarios de Ritchie Annand)
En nuestro caso, detectamos el buggy del controlador VPN y cambiamos Move y FillChar por las versiones de Delphi 7, reemplazamos IntToStr por una versión de Pascal (la versión Int64 usa la FPU) y, como estamos utilizando FastMM, deshabilitamos su configuración personalizada las rutinas de movimiento de tamaño también, ya que son aún más susceptibles que System.Move.
Puede ser un error en su controlador de video que no conserva la palabra de control 8087 cuando realiza la operación de StretchBlt.
En el pasado, he visto un comportamiento similar cuando uso ciertos controladores de impresora. Creen que poseen el 8087 CW y están equivocados ...
Tenga en cuenta que el valor predeterminado del 8087 CW en Delphi parece $ 1372; para una explicación más detallada de los valores de CW, vea este artículo : también explica una situación que Michael Justin describió cuando su 8087CW recibió una manguera.
--jeroen
Solo para su información (en caso de que otra persona también tenga el mismo problema): realizamos una actualización de nuestro software para un cliente y la pantalla táctil completa se bloqueó cuando nuestra aplicación se inició. ¡Windows estaba completamente congelado! La PC tuvo que ser reiniciada (apagado). Tomó algún tiempo descubrir la causa del congelamiento completo.
Afortunadamente, tuvimos una (solo 1) stacktrace de un AV en FastMove.LargeSSEMove. Inhabilité el uso de SSE en fastmove y el problema desapareció.
Por cierto: la pantalla táctil tiene una CPU VIA Nehemiah con un chipset S3.
¡Así que no solo puede obtener daños en la memoria cuando usa la FPU, sino también una congelación completa!