この仕様は、クライアントの JavaScript 実行時間を測定するためのサンプリングプロファイラーを Web アプリケーションが制御できるようにする API を説明します。
現在、複雑な Web アプリケーションからは、クライアント上で JS の 実行時間がどこに費やされているかを限定的にしか把握できません。スタックサンプルを効率的に 収集できないため、アプリケーションは不正確であり、実行を大幅に 低速化させる可能性があるプロファイリング用フックをコードに組み込まざるを得ません。 サンプリングプロファイラーを操作する API を提供することで、 アプリケーションは最小限のオーバーヘッドで、集約および分析に使用できる 詳細な実行データを収集できます。
次の例は、コストの高い処理をユーザーがプロファイリングし、10ms ごとに JS 実行 サンプルを収集する方法を示しています。トレースをサーバーに送信して分析することで、外れ値や JS 実行 特性を集約してデバッグできます。
const profiler = new Profiler({ sampleInterval: 10, maxBufferSize: 10000 });
const start = performance.now();
for (let i = 0; i < 1000000; i++) {
doWork();
}
const duration = performance.now() - start;
const trace = await profiler.stop();
const traceJson = JSON.stringify({
duration,
trace,
});
sendTrace(traceJson);
実際によくあるもう一つのシナリオは、ページ読み込み全体にわたって JS をプロファイリングすることです。この例では onload イベントをプロファイリングし、トレースとともにパフォーマンスタイミングデータを送信します。
const profiler = new Profiler({ sampleInterval: 10, maxBufferSize: 10000 });
window.addEventListener('load', async () => {
const trace = await profiler.stop();
const traceJson = JSON.stringify({
timing: performance.timing,
trace,
});
sendTrace(traceJson);
});
// ページの残りの JS 初期化ロジック
サンプルとは、特定の時点における実行の 瞬間的な状態を表す記述子です。各サンプルは スタックに関連付けられます。
スタックとは、最も外側のフレームから最も内側のフレームまで 順番に並べなければならないフレームのリストです。
フレームとは、現在の実行状態に関する情報を含む スタックのコンテキスト内の要素です。
プロファイリングセッションとは、サンプルを生成する抽象的な生成元です。各セッションは次のものを持ちます。
{started, paused, stopped} のいずれかです。UA はこの頻度でサンプルを取得することを要求されません。ただし、より高品質な トレースを生成するため、この頻度でサンプルを取得するようサンプリングを優先することが 推奨されます。
同じページ上で複数のプロファイリングセッションをサポートすべきです。
started 状態では、UA はサンプル間隔が経過するたびに
サンプルを取得するアルゴリズムを [= in parallel =] 実行し、ベストエフォートでサンプルを取得すべきです。
paused 状態および stopped 状態では、UA はサンプルを取得すべきではありません。
プロファイリングセッションは started 状態で開始しなければなりません。
UA は、セッションを started から paused へ、および paused
から started へ移行してもよいです。
閲覧コンテキストがフォアグラウンドにない場合、ユーザーエージェントは プロファイリングセッションのサンプリングを一時停止することが推奨されます。
stopped のセッションは、started 状態または paused 状態へ移行してはなりません。
プロファイリングセッションを指定してサンプルを取得するには、次の手順を実行します。
stopped に移行して、
return します。
stack に束縛された実行コンテキストスタックを指定して スタック ID を取得するには、次の手順を実行します。
undefined を返します。undefined の場合、parentId を返します。context に束縛された実行 コンテキストを指定してフレーム ID を取得するには、次の手順を実行します。
undefined を返します。ScriptOrModule と等しいものとします。
ScriptOrModule を返します。
上記のロジックの目的は、アクセス不能なスクリプトによって呼び出された組み込み関数が
トレースに公開されないようにすることです。そのため、帰属先としてそれらを呼び出した
ScriptOrModule を使用します。
「|instance| を呼び出した関数を含む ScriptOrModule」は、
より厳密に定義すべきです。これを提供するため、ScriptOrModule を定義する
スタック上の最上位の実行コンテキストを利用できますが、これは理想的ではありません。
理論上、組み込み関数が実行コンテキストスタックにキューされる別の仕組みが存在する可能性があり、
その場合、帰属は無効になります。
undefined を返します。true と等しい場合、
undefined を返します。
この検査により、CORS クロスオリジンレスポンスで提供されたクロスオリジンスクリプトの スタックフレームが含まれないようにします。この用途をより適切に反映するよう、 ミュートエラーの名称変更を検討してもよいでしょう。
list 内の item に対して要素 ID を取得するには、次の手順を実行します。
[Exposed=(Window, Worker)]
interface Profiler : EventTarget {
readonly attribute DOMHighResTimeStamp sampleInterval;
readonly attribute boolean stopped;
constructor(ProfilerInitOptions options);
Promise<ProfilerTrace> stop();
};
各Profilerは、正確に一つのプロファイリングセッションに関連付けられなければなりません。
sampleInterval 属性は、関連付けられたプロファイリングセッションの サンプル間隔を、DOMHighResTimeStampとして表した値を反映しなければなりません。
stopped 属性は、プロファイリングセッションの状態が
stopped である場合に限り、かつその場合に限って true でなければなりません。
RangeError をスローします。
"js-profiling-mode" の
ポリシー値を取得します。
|profilingMode| をその結果とします。
"eager" または "lazy" でない場合:
"js-profiling" の
ポリシー値を取得します。
|jsProfilingEnabled| をその結果とします。
"NotAllowedError"
DOMException をスローします。
"NotAllowedError" DOMException をスローします。
«[{{ProfilerTrace/resources}} → «», {{ProfilerTrace/frames}} → «», {{ProfilerTrace/stacks}} → «», {{ProfilerTrace/samples}} → «»]»
に設定します。
プロファイラーを停止し、トレースを返します。このメソッドは次の手順を実行しなければなりません。
stopped である場合、"InvalidStateError" DOMException で [= a promise
rejected with =] を返します。
stopped に設定します。stop() が呼び出された後に取得されたサンプルは、 プロファイリングセッションに含めるべきではありません。
typedef DOMString ProfilerResource;
dictionary ProfilerTrace {
required sequence<ProfilerResource> resources;
required sequence<ProfilerFrame> frames;
required sequence<ProfilerStack> stacks;
required sequence<ProfilerSample> samples;
};
resources 属性は、サンプルを取得するアルゴリズムによって設定された ProfilerResource リストを返さなければなりません。
frames 属性は、サンプルを取得するアルゴリズムによって設定された ProfilerFrame リストを返さなければなりません。
stacks 属性は、サンプルを取得するアルゴリズムによって設定された ProfilerStack リストを返さなければなりません。
samples 属性は、サンプルを取得するアルゴリズムによって設定された ProfilerSample リストを返さなければなりません。
この表現は、V8 トレース イベント形式 およびGecko プロファイル形式から着想を得ており、 容易かつ効率的にシリアル化できるよう設計されています。
dictionary ProfilerSample {
required DOMHighResTimeStamp timestamp;
unsigned long long stackId;
};
timestamp は、初期化された値を返さなければなりません。
stackId は、初期化された値を返さなければなりません。
dictionary ProfilerStack {
unsigned long long parentId;
required unsigned long long frameId;
};
parentId は、初期化された値を返さなければなりません。
frameId は、初期化された値を返さなければなりません。
dictionary ProfilerFrame {
required DOMString name;
unsigned long long resourceId;
unsigned long long line;
unsigned long long column;
};
name は、初期化された値を返さなければなりません。
resourceId は、初期化された値を返さなければなりません。
line は、初期化された値を返さなければなりません。
column は、初期化された値を返さなければなりません。
dictionary ProfilerInitOptions {
required DOMHighResTimeStamp sampleInterval;
required unsigned long maxBufferSize;
};
ProfilerInitOptions は、次のフィールドをサポートしなければなりません。
この仕様は、プロファイリング機能および初期化動作を制御するため、 文書 ポリシーに構成 ポイントを定義します。
js-profiling-mode
この仕様は、js-profiling-mode という名前の構成
ポイントを定義します。その型は
enum で、許可される値は "eager" および "lazy" です。既定
値は空文字列です。
このポリシーを設定すると、スクリプトが JS セルフプロファイリング API を使用することを許可します。
eager
js-profiling-mode が "eager" に設定されている場合、
ページ読み込み中のプロファイリングが想定されていることを UA に通知します。
UA はこのヒントを使用して、文書読み込み中のできるだけ早い段階で必要なメタデータを保存し、 プロファイリングコンポーネントをウォームアップするなど、プロファイリング基盤を早期に 初期化できます。ただし、プロファイリングが実際に使用されていない場合でも、 無視できないパフォーマンスオーバーヘッドが生じる可能性があります。このオーバーヘッドは、 First Contentful Paint (FCP) や Largest Contentful Paint (LCP) などの指標に 悪影響を及ぼす可能性があります。
lazy
js-profiling-mode が "lazy" に設定されている場合、
プロファイリングが条件付きで使用されることを UA に通知します。
UA はこのヒントを使用して、最初のProfilerがインスタンス化されるまで プロファイリング関連の初期化オーバーヘッドを遅延させることができ、 プロファイリングが実際に使用されていない場合の重要なレンダリング期間中の パフォーマンスコストを回避できます。ただし、ユーザー操作の処理中に初期化が行われると、 Interaction to Next Paint (INP) に悪影響を及ぼす可能性があります。このモードは、 サンプリングの判断に基づいて条件付きでプロファイリングを有効にする文書に特に適しています。
js-profiling(非推奨)
この仕様は、js-profiling という名前の構成
ポイントも定義します。その型は
boolean で、既定
値は false です。
有効にすると、このポリシーはスクリプトが JS セルフプロファイリング API を使用することを許可し、
プロファイリング基盤の早期初期化を実行するよう UA に通知します
(意味的には js-profiling-mode=eager と同等です)。
js-profiling 真偽値構成ポイントは、js-profiling-mode を優先するため
非推奨です。両方が指定された場合、js-profiling-mode が優先され、
js-profiling は無視されなければなりません。実装は後方互換性のために
js-profiling をサポートすべきですが、将来そのサポートを削除してもよいです。
ユーザーエージェントの自動化およびアプリケーションテストのために、この文書は次の [[WebDriver]] 拡張コマンドを定義します。
| HTTP メソッド | URI テンプレート |
|---|---|
| POST | `/session/{session id}/forcesample` |
サンプルを強制取得拡張コマンドは、 より決定論的なテストを可能にするため、すべての [=プロファイリングセッション=] に [=サンプルを取得する=]ことを強制します。
リモート側の手順は次のとおりです。
started である場合、
|session| を指定して [=サンプルを取得する=]を実行します。
null を伴う成功を返します。次の節では、API におけるプライバシーおよびセキュリティ上の選択について詳しく説明し、 さまざまな種類の攻撃に対する保護戦略を示します。
この API は、サンプルを取得するアルゴリズムによって含まれるすべての関数が、
ミュートエラープロパティを通じてCORS 同一オリジンとして提供されたスクリプト内で
定義されていることを要求することで、クロスオリジンスクリプトの内容が公開されるのを防ぎます。
ブラウザーの組み込み関数(performance.now() など)も、
[= CORS-same-origin =] スクリプトから呼び出された場合にのみ含めなければなりません。
その結果、この API は、手動による計測ですでに取得可能な情報を超えて、 クロスオリジンスクリプトの内容や実行特性に関する新たな知見を公開しません。 UA が極端に小さいサンプル間隔値(たとえば 1 ミリ秒未満)をサポートする場合は、 この性質が維持されることを検証することが推奨されます。
クロスオリジン実行コンテキストは、サンプルを取得するアルゴリズム内の
レルム検査を通じて API から観測可能であるべきではありません。したがって、
プロファイラーとエージェントを共有するクロスオリジン iframes および
その他の実行コンテキストの実行は、この API を通じて観測できません。
高解像度のタイミング情報を得る新たな情報源を導入する可能性がある API では、 タイミング攻撃が引き続き懸念事項となります。トレースで収集されるタイムスタンプは、 サイドチャネル攻撃の新たな経路を公開しないよう、[[?HR-Time]] の 現在の高解像度時刻と同じ情報源から取得すべきです。
[[?HR-Time]] によるクロック 解像度の議論を参照してください。