Почему pow () неправильно вычисляет время работы Webkit?

У меня есть приложение Qt C ++, где есть поток GUI, в котором происходит вычисление с плавающей запятой. Также открывается QWebView где есть флеш плеер с некоторым видео.

Очевидно, что закрытие QWebView мешает новой следующей операции с плавающей запятой.
Так pow(double, double) возвращает определенные, но неверные значения.

В одном случае он возвращал значения 1000 раз больше, чем правильный. В другой раз вернул 1.#inf при использовании с аргументами pow(10.0, 2.0),

Я должен упомянуть, что он протестирован на разных компьютерах и не относится к конкретному процессору.

Есть ли у вас какие-либо предложения о том, как найти место в Webkit, которое что-то не так с сопроцессором, и как это предотвратить?

Среда: Qt 4.7.4, C ++, HTML и flowplayer

CPP

wrongpow::wrongpow(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
QVBoxLayout* layout = new QVBoxLayout(0);
m_view = new QWebView(this);
m_view->setMinimumSize(400, 400);
m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
m_view->settings()->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
layout->addWidget(m_view);
QDir dir(QApplication::applicationDirPath());
dir.cd("media");
m_view->load(QUrl(QFileInfo(dir, "index.html").absoluteFilePath()));

QPushButton* button = new QPushButton(QLatin1String("Click on video start"), this);
layout->addWidget(button);
Q_ASSERT(connect(button, SIGNAL(clicked()), this, SLOT(closeView())));

setLayout(layout);
adjustSize();
}
Q_SLOT void wrongpow::closeView()
{
delete m_view;
m_view = NULL;

double wrongResult = pow(10.0, 2.0);
Q_ASSERT(wrongResult == 100.0);
}

HTML

<div id='player' style='width:100%; height:100%;'>
<object width='100%' height='100%' id='_494187117' name='_494187117' data='js/plugins/flowplayer-3.2.18.swf' type='application/x-shockwave-flash'>
<param name='wmode' value='opaque'>
<param name='flashvars' value='config={&quot;clip&quot;:{&quot;url&quot;:&quot;mp4:vod/demo.flowplayer/buffalo_soldiers.mp4&quot;,&quot;scaling&quot;:&quot;fit&quot;,&quot;provider&quot;:&quot;hddn&quot;,&quot;live&quot;:true},&quot;plugins&quot;:{&quot;hddn&quot;:{&quot;url&quot;:&quot;js/plugins/flowplayer.rtmp-3.2.13.swf&quot;,&quot;netConnectionUrl&quot;:&quot;rtmp://r.demo.flowplayer.netdna-cdn.com/play&quot;}},&quot;canvas&quot;:{&quot;backgroundGradient&quot;:&quot;none&quot;}}'>
</object>
</div>

Вот полностью рабочая программа с источниками:
Скачать 15 МБ

11

Решение

Причина неверного результата pow в том, что x87 регистры ST0-ST3 стали 1#SNAN и так TAGS = 0xFF. Это происходит при уничтожении QWebView, содержащего объект флеш-видео. Контрольные слова x87 и SSE содержат правильные значения.
Протестированная библиотека Adove Flash: NPSWF64_14_0_0_125.dll

Бывает когда WebCore::PluginView::stop метод вызывает деструктор объекта плагина Adobe Flash.

NPError npErr = m_plugin->pluginFuncs()->destroy(m_instance, &savedData);

Вот процедура (NPSWF64.dll), которая портит регистры (фактически она использует регистры MMX, связанные с регистрами x87):

mov         qword ptr [rsp+8],rcx
mov         qword ptr [rsp+10h],rdx
mov         qword ptr [rsp+18h],r8
push        rsi
push        rdi
push        rbx
mov         rdi,qword ptr [rsp+20h]
mov         rsi,qword ptr [rsp+28h]
mov         rcx,qword ptr [rsp+30h]
cmp         rcx,20h
jle         000000002F9D8A2D
sub         rcx,20h

// writes wrong values to the registers:
movq        mm0,mmword ptr [rsi]
movq        mm1,mmword ptr [rsi+8]
movq        mm2,mmword ptr [rsi+10h]
movq        mm3,mmword ptr [rsi+18h]

add         rsi,20h
movq        mmword ptr [rdi],mm0
movq        mmword ptr [rdi+8],mm1
movq        mmword ptr [rdi+10h],mm2
movq        mmword ptr [rdi+18h],mm3
add         rdi,20h
sub         rcx,20h
jge         000000002F9D89F1
add         rcx,20h
rep movs    byte ptr [rdi],byte ptr [rsi]
pop         rbx
pop         rdi
pop         rsi
ret

Для предотвращения неправильного расчета pow вызванная этой ошибкой, необходимо восстановить значения регистра. Я использую самый простой способ сделать это. Когда плагин уничтожен, я звоню pow с некоторыми аргументами, и это восстанавливает регистры. Следующий звонок будет правильным.
Существует более сложный (но, вероятно, правильный) способ сделать то же самое, записав новые значения в регистры, используя методы из float.h библиотека.

6

Другие решения

Просто чтобы добавить к этому немного, я думаю, что я столкнулся с той же проблемой (в 64-битном приложении C #, которое вызывает Math.Cos, а также показывает Flash-видео в элементе управления .NET Windows Forms WebBrowser).

Точно так же, в моем случае, кажется, что следующий код в 64-битной Flash OCX (16.0.0.305) оставляет недопустимые значения в регистрах MM0, MM1, MM2, MM3 (этот код является своего рода быстрым копированием памяти) :

C:\Windows\System32\Macromed\Flash\Flash64_16_0_0_305.ocx
00000000`2f9833c0 48894c2408      mov     qword ptr [rsp+8],rcx
00000000`2f9833c5 4889542410      mov     qword ptr [rsp+10h],rdx
00000000`2f9833ca 4c89442418      mov     qword ptr [rsp+18h],r8
00000000`2f9833cf 56              push    rsi
00000000`2f9833d0 57              push    rdi
00000000`2f9833d1 53              push    rbx
00000000`2f9833d2 488b7c2420      mov     rdi,qword ptr [rsp+20h]
00000000`2f9833d7 488b742428      mov     rsi,qword ptr [rsp+28h]
00000000`2f9833dc 488b4c2430      mov     rcx,qword ptr [rsp+30h]
00000000`2f9833e1 4881f920000000  cmp     rcx,20h
00000000`2f9833e8 7e43            jle     Flash64_16_0_0_305!DllUnregisterServer+0x3c2a2d (00000000`2f98342d)
00000000`2f9833ea 4881e920000000  sub     rcx,20h
00000000`2f9833f1 0f6f06          movq    mm0,mmword ptr [rsi]
00000000`2f9833f4 0f6f4e08        movq    mm1,mmword ptr [rsi+8]
00000000`2f9833f8 0f6f5610        movq    mm2,mmword ptr [rsi+10h]
00000000`2f9833fc 0f6f5e18        movq    mm3,mmword ptr [rsi+18h]
00000000`2f983400 4881c620000000  add     rsi,20h
00000000`2f983407 0f7f07          movq    mmword ptr [rdi],mm0
00000000`2f98340a 0f7f4f08        movq    mmword ptr [rdi+8],mm1
00000000`2f98340e 0f7f5710        movq    mmword ptr [rdi+10h],mm2
00000000`2f983412 0f7f5f18        movq    mmword ptr [rdi+18h],mm3
00000000`2f983416 4881c720000000  add     rdi,20h
00000000`2f98341d 4881e920000000  sub     rcx,20h
00000000`2f983424 7dcb            jge     Flash64_16_0_0_305!DllUnregisterServer+0x3c29f1 (00000000`2f9833f1)
00000000`2f983426 4881c120000000  add     rcx,20h
00000000`2f98342d f3a4            rep movs byte ptr [rdi],byte ptr [rsi]
00000000`2f98342f 5b              pop     rbx
00000000`2f983430 5f              pop     rdi
00000000`2f983431 5e              pop     rsi
00000000`2f983432 c3              ret

Приведенный выше код во Flash OCX выполняется по мере того, как веб-браузер перемещается со страницы, показывающей Flash-видео.

Перед выполнением этого кода регистры с плавающей запятой были следующими (проверено с помощью WinDbg.exe):

fpcw=027f fpsw=3820 fptw=0080
st0= 6.00000000000000000000000...0e+0001 (0:4004:f000000000000000)
st1= 0.00000000000000000000000...0e+0000 (0:0000:0000000000000000)
st2= 0.00000000000000000000000...0e+0000 (0:0000:0000000000000000)
st3= 0.00000000000000000000000...0e+0000 (0:0000:0000000000000000)
st4= 0.00000000000000000000000...0e+0000 (0:0000:0000000000000000)
st5= 1.00000000000000000000000...0e+0000 (0:3fff:8000000000000000)
st6= 8.94231504669236176852000...0e-0001 (0:3ffe:e4ec5b1b9b742000)
st7= 0.00000000000000000000000...0e+0000 (0:0000:0000000000000000)
mxcsr=00001fa4

После выполнения вышеуказанного кода регистры с плавающей запятой выглядели следующим образом:

fpcw=027f fpsw=0020 fptw=00ff
st0=-1.#SNAN000000000000000000...0e+0000 (1:7fff:0000000000000000)
st1=-1.#SNAN000000000000000000...0e+0000 (1:7fff:0000000000000000)
st2=-1.#SNAN000000000000000000...0e+0000 (1:7fff:0000000000000000)
st3=-1.#SNAN000000000000000000...0e+0000 (1:7fff:0000000000000000)
st4= 1.00000000000000000000000...0e+0000 (0:3fff:8000000000000000)
st5= 8.94231504669236176852000...0e-0001 (0:3ffe:e4ec5b1b9b742000)
st6= 0.00000000000000000000000...0e+0000 (0:0000:0000000000000000)
st7= 6.00000000000000000000000...0e+0001 (0:4004:f000000000000000)
mxcsr=00001fa4

В этот момент регистры с плавающей запятой кажутся в «поврежденном» состоянии.

Итак, позже, вернувшись в программу на C #, при выполнении Math.Cos код операции fld завершается неудачно, когда он пытается поместить значение 2.0 в стек с плавающей запятой (то есть, когда он пытается подготовиться к вычислению косинуса 2.0):

COMDouble::Cos:
000007FE`E1D01570  movsd   mmword ptr [rsp+8],xmm0
000007FE`E1D01576  fld     qword ptr [rsp+8]
000007FE`E1D0157A  fcos
000007FE`E1D0157C  fstp    qword ptr [rsp+8]
000007FE`E1D01580  movsd   xmm0,mmword ptr [rsp+8]
000007FE`E1D01586  ret

Непосредственно перед выполнением кода операции fld регистры с плавающей запятой были в точности такими, какими они были оставлены Flash OCX (т. Е. Как указано выше):

fpcw=027f fpsw=0020 fptw=00ff
st0=-1.#SNAN000000000000000000...0e+0000 (1:7fff:0000000000000000)
st1=-1.#SNAN000000000000000000...0e+0000 (1:7fff:0000000000000000)
st2=-1.#SNAN000000000000000000...0e+0000 (1:7fff:0000000000000000)
st3=-1.#SNAN000000000000000000...0e+0000 (1:7fff:0000000000000000)
st4= 1.00000000000000000000000...0e+0000 (0:3fff:8000000000000000)
st5= 8.94231504669236176852000...0e-0001 (0:3ffe:e4ec5b1b9b742000)
st6= 0.00000000000000000000000...0e+0000 (0:0000:0000000000000000)
st7= 6.00000000000000000000000...0e+0001 (0:4004:f000000000000000)
mxcsr=00001fa4

Сразу после выполнения кода операции fld регистры с плавающей запятой выглядели следующим образом (обратите внимание, что st0 = #IND, а не ожидаемое значение 2.00000000000000000000000 … 0e + 0000):

fpcw=027f fpsw=3a61 fptw=00ff
st0=-1.#IND0000000000000000000...0e+0000 (1:7fff:c000000000000000)
st1=-1.#SNAN000000000000000000...0e+0000 (1:7fff:0000000000000000)
st2=-1.#SNAN000000000000000000...0e+0000 (1:7fff:0000000000000000)
st3=-1.#SNAN000000000000000000...0e+0000 (1:7fff:0000000000000000)
st4=-1.#SNAN000000000000000000...0e+0000 (1:7fff:0000000000000000)
st5= 1.00000000000000000000000...0e+0000 (0:3fff:8000000000000000)
st6= 8.94231504669236176852000...0e-0001 (0:3ffe:e4ec5b1b9b742000)
st7= 0.00000000000000000000000...0e+0000 (0:0000:0000000000000000)
mxcsr=00001fa4

Это означает, что код операции fcos пытается вычислить косинус #IND. И так получается, что возвращаемое значение Math.Cos в C # равно Double.NaN вместо ожидаемого -0.416146836547142.

Отключение значения слова состояния с плавающей запятой (FPSW) от 0x3a61 выше указывает на то, что проблема заключается в «попытке загрузить значение в несвободный регистр»:

3a61: 0011 1010 0110 0001

TOP (13,12,11):          111
C3,C2,C1,C0 (14,10,9,8): 0 010  (ie. C1 is 1)  <-- loading a value into a register which is not free
IR (7):                  0  Interrupt Request
SF (6):                  1  Stack Fault  <-- loading a value into a register which is not free
P (5):                   1  Precision
U (4):                   0  Underflow
O (3):                   0  Overflow
Z (2):                   0  Zero Divide
D (1):                   0  Denormalised
I (0):                   1  Invalid Operation

Обратите внимание, что проблема возникает только при первой попытке Math.Cos (2) (т. Е. Вскоре после перехода от страницы видео Flash). Вторая и последующие попытки вычислить Math.Cos (2) увенчались успехом.

Проблема также возникает, если элемент управления WebBrowser расположен во время воспроизведения видео Flash (или приостановлено).

Итак, некоторые возможные обходные пути:

  1. Выполните одно фиктивное математическое вычисление и проигнорируйте результат.

  2. Напишите 64-битную DLL, которая экспортирует функцию, которая выполняет коды операций finit или emms (для сброса состояния регистров с плавающей запятой). Вызовите эту функцию из C #.

  3. Запустите как 32-битный процесс.

Примечание: создание фиктивного исключения в C # с последующим перехватом этого исключения не помогло (это рекомендуется для других проблем с плавающей запятой).

Вот некоторый код C #, который воспроизводит проблему (убедитесь, что она компилируется и работает как 64-битная; нажмите кнопку «Рассчитать» после начала воспроизведения видео).

using System;
using System.IO;
using System.Windows.Forms;

namespace FlashTest
{
public partial class TestForm : Form
{
public TestForm()
{
// Windows 7 SP1 64 bit
// Internet Explorer 11 (11.0.9600.17633)
// Flash Player 16.0.0.305
// Visual Studio 2013 (12.0.31101.00)
// .NET 4.5 (4.0.30319.34209)

InitializeComponent();
addressTextBox.Text = "http://www.youtube.com/v/JVGdyC9CvFQ?autoplay=1";
GoButtonClickHandler(this, EventArgs.Empty);
}

private void GoButtonClickHandler(object sender, EventArgs e)
{
string path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".html");
File.WriteAllText(path, string.Format(@"<html><body>
<object classid=""clsid:D27CDB6E-AE6D-11CF-96B8-444553540000"" width=""100%"" height=""100%"" id=""youtubeviewer"">
<param name=""movie"" value=""{0}"">
</object></body></html>", addressTextBox.Text));
webBrowser.Navigate(path);
}

private void CalculateButtonClickHandler(object sender, EventArgs e)
{
webBrowser.DocumentCompleted += DocumentCompletedHandler;
webBrowser.Navigate("about:blank");
}

private void DocumentCompletedHandler(object sender, WebBrowserDocumentCompletedEventArgs e)
{
webBrowser.DocumentCompleted -= DocumentCompletedHandler;
MessageBox.Show("Math.Cos(2) returned " + Math.Cos(2));
}
}
}

namespace FlashTest
{
partial class TestForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.webBrowser = new System.Windows.Forms.WebBrowser();
this.addressTextBox = new System.Windows.Forms.TextBox();
this.goButton = new System.Windows.Forms.Button();
this.calculateButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// webBrowser
//
this.webBrowser.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.webBrowser.Location = new System.Drawing.Point(12, 41);
this.webBrowser.MinimumSize = new System.Drawing.Size(20, 20);
this.webBrowser.Name = "webBrowser";
this.webBrowser.Size = new System.Drawing.Size(560, 309);
this.webBrowser.TabIndex = 3;
//
// addressTextBox
//
this.addressTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.addressTextBox.Location = new System.Drawing.Point(12, 14);
this.addressTextBox.Name = "addressTextBox";
this.addressTextBox.Size = new System.Drawing.Size(398, 20);
this.addressTextBox.TabIndex = 0;
//
// goButton
//
this.goButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.goButton.Location = new System.Drawing.Point(416, 12);
this.goButton.Name = "goButton";
this.goButton.Size = new System.Drawing.Size(75, 23);
this.goButton.TabIndex = 1;
this.goButton.Text = "&Go";
this.goButton.UseVisualStyleBackColor = true;
this.goButton.Click += new System.EventHandler(this.GoButtonClickHandler);
//
// calculateButton
//
this.calculateButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.calculateButton.Location = new System.Drawing.Point(497, 12);
this.calculateButton.Name = "calculateButton";
this.calculateButton.Size = new System.Drawing.Size(75, 23);
this.calculateButton.TabIndex = 2;
this.calculateButton.Text = "&Calculate";
this.calculateButton.UseVisualStyleBackColor = true;
this.calculateButton.Click += new System.EventHandler(this.CalculateButtonClickHandler);
//
// TestForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(584, 362);
this.Controls.Add(this.webBrowser);
this.Controls.Add(this.goButton);
this.Controls.Add(this.addressTextBox);
this.Controls.Add(this.calculateButton);
this.Name = "TestForm";
this.Text = "Adobe Flash Test";
this.ResumeLayout(false);
this.PerformLayout();

}

#endregion

private System.Windows.Forms.WebBrowser webBrowser;
private System.Windows.Forms.TextBox addressTextBox;
private System.Windows.Forms.Button goButton;
private System.Windows.Forms.Button calculateButton;
}
}
2

Видимо, вы столкнулись с ошибкой в ​​версии вашего Qt. Я не могу воспроизвести в QT 5.3 со сборками QtCreator, используя MSVC 13 x64 в выпуске и отладке.

Для QtWebKit уже есть сообщения об ошибках с плавающей запятой:

1
По вопросам рекламы [email protected]