Electron memory tracing - electron

I would like to check an Electron app memory usage by chrome://tracing.
I tried to apply content tracing example from the documentation
with passing TraceCategoriesAndOptions and TraceConfig objects.
However, in both cases I got trace output without memory info.
Am I doing something wrong? Should I pass some additional args?
Trace output:
{"traceEvents":[
{"args":{"name":"swapper"},"cat":"__metadata","name":"thread_name","ph":"M","pid":3624,"tid":0,"ts":0},
{"args":{"name":"ThreadPoolForegroundWorker"},"cat":"__metadata","name":"thread_name","ph":"M","pid":3624,"tid":18088,"ts":0},
{"args":{"name":"ThreadPoolForegroundWorker"},"cat":"__metadata","name":"thread_name","ph":"M","pid":16656,"tid":18548,"ts":0},
{"args":{"name":"ThreadPoolForegroundWorker"},"cat":"__metadata","name":"thread_name","ph":"M","pid":18348,"tid":19212,"ts":0},
{"args":{"name":"ThreadPoolForegroundWorker"},"cat":"__metadata","name":"thread_name","ph":"M","pid":1864,"tid":18452,"ts":0},
{"args":{"name":"ThreadPoolForegroundWorker"},"cat":"__metadata","name":"thread_name","ph":"M","pid":17516,"tid":18420,"ts":0},
{"args":{"name":"Service: network.mojom.NetworkService"},"cat":"__metadata","name":"process_name","ph":"M","pid":1864,"tid":0,"ts":0},
{"args":{"name":"Browser"},"cat":"__metadata","name":"process_name","ph":"M","pid":16656,"tid":0,"ts":0},
{"args":{"name":"Service: tracing.mojom.TracingService"},"cat":"__metadata","name":"process_name","ph":"M","pid":3624,"tid":0,"ts":0},
{"args":{"name":"GPU Process"},"cat":"__metadata","name":"process_name","ph":"M","pid":18348,"tid":0,"ts":0},
{"args":{"name":"Renderer"},"cat":"__metadata","name":"process_name","ph":"M","pid":17516,"tid":0,"ts":0},
{"args":{"number":12},"cat":"__metadata","name":"num_cpus","ph":"M","pid":3624,"tid":0,"ts":5701931484},
{"args":{"number":12},"cat":"__metadata","name":"num_cpus","ph":"M","pid":16656,"tid":0,"ts":5701931504},
{"args":{"number":12},"cat":"__metadata","name":"num_cpus","ph":"M","pid":18348,"tid":0,"ts":5701931510},
{"args":{"number":12},"cat":"__metadata","name":"num_cpus","ph":"M","pid":1864,"tid":0,"ts":5701931542},
{"args":{"number":12},"cat":"__metadata","name":"num_cpus","ph":"M","pid":17516,"tid":0,"ts":5701931566},
{"args":{"sort_index":-1},"cat":"__metadata","name":"process_sort_index","ph":"M","pid":18348,"tid":19212,"ts":5701931572},
{"args":{"uptime":10},"cat":"__metadata","name":"process_uptime_seconds","ph":"M","pid":18348,"tid":19212,"ts":5701931574},
{"args":{"sort_index":-6},"cat":"__metadata","name":"process_sort_index","ph":"M","pid":16656,"tid":18548,"ts":5701931576},
{"args":{"uptime":10},"cat":"__metadata","name":"process_uptime_seconds","ph":"M","pid":3624,"tid":18088,"ts":5701931578},
{"args":{"uptime":10},"cat":"__metadata","name":"process_uptime_seconds","ph":"M","pid":16656,"tid":18548,"ts":5701931579},
{"args":{"uptime":10},"cat":"__metadata","name":"process_uptime_seconds","ph":"M","pid":1864,"tid":18452,"ts":5701931613},
{"args":{"sort_index":-5},"cat":"__metadata","name":"process_sort_index","ph":"M","pid":17516,"tid":16752,"ts":5701931621},
{"args":{"uptime":10},"cat":"__metadata","name":"process_uptime_seconds","ph":"M","pid":17516,"tid":16752,"ts":5701931624},
{"args":{"labels":"Hello World!"},"cat":"__metadata","name":"process_labels","ph":"M","pid":17516,"tid":16752,"ts":5701931626},
{"args":{"sort_index":-1},"cat":"__metadata","name":"thread_sort_index","ph":"M","pid":17516,"tid":16752,"ts":5701931627}],"metadata":
{"chrome-bitness":64,"clock-domain":"WIN_QPC","command_line":"\"D:\\MemoryUsage\\POCs\\testApp2\\node_modules\\electron\\dist\\electron.exe\" --allow-file-access-from-files .","cpu-brand":"Intel(R) Core(TM) i7-9750H CPU # 2.60GHz","cpu-family":6,"cpu-model":158,"cpu-stepping":10,"gpu-devid":0,"gpu-driver":"","gpu-psver":"","gpu-venid":0,"gpu-vsver":"","highres-ticks":1,"network-type":"Unknown","num-cpus":12,"os-arch":"x86_64","os-name":"Windows NT","os-session":"local","os-version":"10.0.19041","os-wow64":"disabled","physical-memory":16303,"product-version":"Chrome/87.0.4280.67","trace-capture-datetime":"2020-12-1 11:22:49","trace-config":"{\"enable_argument_filter\":false,\"enable_systrace\":false,\"excluded_categories\":[\"*\"],\"record_mode\":\"record-until-full\"}","trace_processor_stats":{"android_log_num_failed":0,"android_log_num_skipped":0,"android_log_num_total":0,"clock_sync_cache_miss":14,"clock_sync_failure":0,"compact_sched_has_parse_errors":0,"compact_sched_switch_skipped":0,"compact_sched_waking_skipped":0,"counter_events_out_of_order":0,"empty_chrome_metadata":0,"flow_duplicate_id":0,"flow_end_without_start":0,"flow_invalid_id":0,"flow_no_enclosing_slice":0,"flow_step_without_start":0,"flow_without_direction":0,"ftrace_bundle_tokenizer_errors":0,"ftrace_packet_before_tracing_start":0,"fuchsia_invalid_event":0,"fuchsia_non_numeric_counters":0,"fuchsia_timestamp_overflow":0,"gpu_counters_invalid_spec":0,"gpu_counters_missing_spec":0,"gpu_render_stage_parser_errors":0,"graphics_frame_event_parser_errors":0,"guess_trace_type_duration_ns":1500,"heap_graph_location_parse_error":0,"heap_graph_non_finalized_graph":0,"heapprofd_missing_packet":0,"heapprofd_non_finalized_profile":0,"interned_data_tokenizer_errors":0,"invalid_clock_snapshots":0,"invalid_cpu_times":0,"json_tokenizer_failure":0,"meminfo_unknown_keys":0,"metatrace_overruns":0,"mismatched_sched_switch_tids":0,"misplaced_end_event":0,"mm_unknown_type":0,"ninja_parse_errors":0,"packages_list_has_parse_errors":0,"packages_list_has_read_errors":0,"parse_trace_duration_ns":275800,"perf_samples_skipped":0,"perf_samples_skipped_dataloss":0,"power_rail_unknown_index":0,"proc_stat_unknown_counters":0,"process_tracker_errors":0,"rss_stat_negative_size":0,"rss_stat_unknown_keys":0,"rss_stat_unknown_thread_for_mm_id":0,"sched_switch_out_of_order":0,"sched_waking_out_of_order":0,"slice_out_of_order":0,"stackprofile_invalid_callstack_id":0,"stackprofile_invalid_frame_id":0,"stackprofile_invalid_mapping_id":0,"stackprofile_invalid_string_id":0,"stackprofile_parser_error":0,"systrace_parse_failure":0,"task_state_invalid":0,"thread_time_in_state_out_of_order":0,"thread_time_in_state_unknown_cpu_freq":0,"tokenizer_skipped_packets":0,"traced_buf":[{"buffer_size":209715200,"bytes_overwritten":0,"bytes_read":688128,"bytes_written":688128,"chunks_committed_out_of_order":0,"chunks_discarded":0,"chunks_overwritten":0,"chunks_read":21,"chunks_rewritten":0,"chunks_written":21,"padding_bytes_cleared":0,"padding_bytes_written":0,"patches_failed":0,"patches_succeeded":0,"readaheads_failed":0,"readaheads_succeeded":0,"trace_writer_packet_loss":0,"write_wrap_count":0}],"traced_chunks_discarded":0,"traced_data_sources_registered":11,"traced_data_sources_seen":6,"traced_patches_discarded":0,"traced_producers_connected":5,"traced_producers_seen":0,"traced_total_buffers":1,"traced_tracing_sessions":1,"track_event_parser_errors":0,"track_event_tokenizer_errors":0,"vmstat_unknown_keys":0,"vulkan_allocations_invalid_string_id":0},"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) POC2/1.0.0 Chrome/87.0.4280.67 Electron/11.0.3 Safari/537.36","v8-version":"8.7.220.25"}}
here is a source code that I tried:
const { app, contentTracing, BrowserWindow } = require("electron");
const url = require("url");
const path = require("path");
const options1 = {
traceOptions: "record-until-full",
categoryFilter: "-*,disabled-by-default-memory-infra", //exclude all category, set memory-infra only
};
const options2 = {
recording_mode: "record-until-full",
enable_argument_filter: false,
include_categories: ["disabled-by-default-memory-infra"],
excluded_categories: ["*"],
memory_dump_config: {
triggers: [
{ mode: "light", periodic_interval_ms: 50 },
{ mode: "detailed", periodic_interval_ms: 1000 },
{ mode: "background", periodic_interval_ms: 1000 },
],
},
};
app.whenReady().then(() => {
(async () => {
console.log("Tracing started");
await contentTracing.startRecording(options2);
await new Promise((resolve) => setTimeout(resolve, 10000));
const path$$1 = await contentTracing.stopRecording();
console.log("Tracing data recorded to " + path$$1);
})();
});
const createWindow = () => {
win = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
},
});
win.loadURL(
url.format({
pathname: path.join(__dirname, "index.html"),
protocol: "file:",
slashes: true,
})
);
};
try {
app.on("ready", createWindow);
} catch (e) {
console.err(e);
}

Related

ReferenceError: document is not defined. Service worker. Workbox

I'm learning how to write code of service worker and stuck with the error "ReferenceError: document is not defined" in my app.js file. I'm using workbox library with InjectManifest mode. I think the problem in the webpack.config.js, because when I delete InjectManifest in webpack.config.js the error disappears.
My webpack.config.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const {InjectManifest} = require('workbox-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.html$/i,
use: [
{
loader: 'html-loader',
},
],
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, 'css-loader',
],
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
},
},
],
},
],
},
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin(),
],
},
plugins: [
new HtmlWebPackPlugin({
template: './src/index.html',
filename: './index.html',
}),
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
}),
new InjectManifest({
swSrc: './src/js/service.worker.js',
swDest: 'service.worker.js',
}),
],
};
My service.worker.js file:
import { precacheAndRoute } from 'workbox-precaching/precacheAndRoute';
import { cinemaNews } from './cinemaNews';
import { url } from './app';
precacheAndRoute(self.__WB_MANIFEST);
const CACHE_NAME = 'v1';
const responseCache = new Response(JSON.stringify(cinemaNews));
self.addEventListener('install', (evt) => {
console.log('install')
evt.waitUntil((async () => {
console.log('install waitUntil')
const cache = await caches.open(CACHE_NAME);
await cache.put(url, responseCache);
await self.skipWaiting();
})());
});
self.addEventListener('activate', (evt) => {
console.log('activate')
evt.waitUntil(self.clients.claim());
});
self.addEventListener('fetch', (evt) => {
console.log('sw fetch')
const requestUrl = new URL(evt.request.url);
if (!requestUrl.pathname.startsWith('/news')) return;
evt.respondWith((async () => {
console.log('respondWith')
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(evt.request);
return cachedResponse;
})());
evt.waitUntil((async () => {
console.log('waitUntil');
const response = await fetch(evt.request.url);
const client = await clients.get(evt.clientId);
let json = await response.json();
client.postMessage(json);
})());
});
This statement:
import { url } from './app';
appears to be triggering the issue, as there must be code inside of your app.js that is executed via that import, and which assumes that document will be defined. (It's not defined inside of the ServiceWorkerGlobalScope.)
Based on how you're using the export, I'm assuming that it's just a string constant containing a shared URL that you want to use from both your main web app and your service worker. Assuming that's the case, the easiest thing to do would be to refactor your modules that there's a constants.js (or some similar name) module that only exports your string constants, and doesn't try to run any code that references document. You can then import the constant from either your web app or the service worker without issue.
// constants.js
export const url = '/path/to/url';
// service-worker.js
import {url} from './constants';
// do something with url
// app.js
import {url} from './constants';
// do something with url

Disable Javascript in Playwright

Is it possible to define a browser with Javascript disabled to simulate how a crawler would view a page? There should be an option that can be passed.
You can pass javaScriptEnabled in the BrowserContext options:
const playwright = require("playwright");
(async () => {
const browser = await playwright.chromium.launch();
const context = await browser.newContext({
javaScriptEnabled: false
});
const page = await context.newPage();
// ...
await browser.close();
})();
In the case of #playwright/test (where you don't define browser.newContext yourself) you can set javaScriptEnabled in testOptions.
(1) In the playwright.config.js file:
const config = {
use: {
headless: false,
javaScriptEnabled: false
},
};
module.exports = config;
(2) or with test.use:
const { test, expect } = require('#playwright/test');
test.use({ javaScriptEnabled: false });
test('website without JavaScript', async ({ page }) => {
// ...
});

Puppeteer page.goto error only with readFileSync

When using await page.goto('http://www.URL.edu') a PDF is generated, but when loading the same URL from a csv file, Puppeteer returns error.
Thought is might have been either a timing issue or a redirect from http to https, but the script proves both not to be the problem.
The commented out is where the url is loaded from a single CSV file with one row: "1,oldwestbury.edu,SUNY College at Old Westbury"
var dir1 = './screenshots';
var dir2 = './pdfs';
const fs = require('fs');
if (!fs.existsSync(dir1)) {
fs.mkdirSync(dir1);
}
if (!fs.existsSync(dir2)) {
fs.mkdirSync(dir2);
}
function readURLFile(path) {
return fs.readFileSync(path, 'utf-8')
.split('\n')
.map((elt) => {
const url = elt.split(',')[1].replace('\r', '');
return `http://${url.toLowerCase()}`;
});
}
const puppeteer = require('puppeteer');
(async () => {
const startDate = new Date().getTime();
const USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3239.108 Safari/537.36';
const urls = readURLFile('./UNVurls.csv');
const browser = await puppeteer.launch({
headless: true
});
for (let url of urls) {
console.log(`Visiting url: ${url}`);
let page = await browser.newPage();
try {
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 2 });
await page.goto('http://www.oldwestbury.edu')
// await page.goto(url, {
// waitUntil: 'networkidle2',
// timeout: 0
// });
let fileName = url.replace(/(\.|\/|:|%|#)/g, "_");
if (fileName.length > 100) {
fileName = fileName.substring(0, 100);
}
await page.waitForSelector('title');
await page.screenshot({
path: `./screenshots/${fileName}.jpeg`,
omitBackground: true
});
await page.emulateMedia('screen');
await page.pdf({
path: `./pdfs/${fileName}.pdf`,
pageRanges: "1",
format: 'A4',
printBackground: true
});
} catch (err) {
console.log(`An error occured on url: ${url}`);
} finally {
await page.close();
}
}
await browser.close();
console.log(`Time elapsed ${Math.round((new Date().getTime() - startDate) / 1000)} s`);
})();
Hoping to determine WHY the PDF is created when using the url direct and why the page load fails when retrieving from CSV file.

Getting "bad-precaching-response " error after service worker registeration is successful

In my project, I am using NextJS+KOA+Apollo. My nextJS app is inside client in root directory. I am using next-offline to convert it to PWA.
Nextjs app is inside client directory. koa server is inside server directory.
when i am building the app via below command:
next build client && tsc --project tsconfig.server.json
it creates a build directory inside client for nextjs and dist directory at the top level for koa server.
i run the code in production via below command
NODE_ENV=production node dist/server/index.js
ISSUE
Service worker is getting registered properly. But I am getting below error:
PrecacheController.mjs:194
Uncaught (in promise) bad-precaching-response: bad-precaching-response :: [{"url":"https://my-domain/_next/bo.svg?__WB_REVISION__=e02afe0476bb357aebde18136fda06e0","status":404}]
at l.o (https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-precaching.prod.js:1:1749)
at async Promise.all (index 0)
at async l.install (https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-precaching.prod.js:1:1221)
Below is my build file that gets generated:
tsconfig.server.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "dist",
"target": "es2017",
"isolatedModules": false,
"noEmit": false
},
"include": ["server/**/*.ts"]
}
Below is my next.config.js (inside client direcotry)
/* eslint-disable #typescript-eslint/no-var-requires */
const withPlugins = require("next-compose-plugins");
const offline = require("next-offline");
const pino = require("next-pino");
const withTypescript = require("#zeit/next-typescript");
const withCSS = require("#zeit/next-css");
const withLess = require("#zeit/next-less");
const Dotenv = require("dotenv-webpack");
const path = require("path");
const _ = require("lodash");
const nextConfig = {
distDir: "build",
webpack(config) {
config.module.rules.push({
test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
use: {
loader: "url-loader",
options: {
limit: 100000,
name: "[name].[ext]",
},
},
});
config.plugins.push(
new Dotenv({
path: path.resolve(process.cwd(), ".env"),
systemvars: true,
}),
);
return config;
},
// overwrites values given in the .env file with the current
// process.env value
env: _.omitBy(
{
GRAPHQL_SERVER: process.env.GRAPHQL_SERVER,
},
_.isUndefined,
),
workboxOpts: {
globPatterns: ["static/**/*"],
globDirectory: "client",
runtimeCaching: [
{
urlPattern: /^https?.*/,
handler: "NetworkFirst",
options: {
cacheName: "offlineCache",
expiration: {
maxEntries: 200,
},
},
},
],
},
};
const cssConfig = {
cssModules: true,
cssLoaderOptions: {
importLoaders: 1,
localIdentName: "[local]",
},
};
const lessConfig = cssConfig;
module.exports = withPlugins(
[
[offline],
[pino],
[withTypescript],
[withCSS, cssConfig],
[withLess, lessConfig],
],
nextConfig,
);
And below is my file to start koa server
import Router from "koa-router";
const server = new Koa();
const dev = !["production", "staging"].includes(process.env.NODE_ENV || "");
const app = next({ dir: "./client", dev });
const publicRouter = new Router();
const handle = app.getRequestHandler();
publicRouter.get("/service-worker.js", async ctx => {
const pathname = await join(
__dirname,
"../../../client/build",
"service-worker.js",
);
ctx.body = await app.serveStatic(ctx.req, ctx.res, pathname);
ctx.respond = false;
});
publicRouter.get("*", async ctx => {
if (!ctx.path.match(/graphql/)) {
await handle(ctx.req, ctx.res);
ctx.respond = false;
}
});
server.use(async (ctx, next) => {
ctx.res.statusCode = 200;
await next();
});
server.use(publicRouter.routes()).use(publicRouter.allowedMethods());
server.listen({ port: 3000 });
================================================================
I have done a dirty fix for now. I am not sure how to handle it properly. I will really appreciate if anyone can put forth their view on this.
As bo.svg, firfox.svg, all these static files are throwing 404,
Ex - (/_next/bo.svg?WB_REVISION=e02afe0476bb357aebde18136fda06e0)
in my file to start koa server, added a condition to check this URL and serve static file from build directory like below:
publicRouter.get("*", async ctx => {
if (ctx.path.match(/\_next/) && ctx.path.match(/\.svg/)) {
const pathname = await join(
__dirname,
"../../../client/build",
ctx.path.replace("_next/", ""),
);
ctx.body = await app.serveStatic(ctx.req, ctx.res, pathname);
ctx.respond = false;
} else if (!ctx.path.match(/graphql/)) {
await handle(ctx.req, ctx.res);
ctx.respond = false;
}
});
It served my prupose for now, but not sure how to handle this properly.

electron menu accelerator not working

I am going through the Electron Fundamentals course on Pluralsight (Trent, 2016). I can't get the accelerator to work on my "Quit" menu item. Below is my entire main.js file. The menu is created successfully from what I can tell (picture below), and clicking directly on the Quit menu item does shut down the application, but the Alt+W key combination does not. I am on Windows 10. What am I missing?
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const Menu = electron.Menu;
app.on('ready', _ => {
new BrowserWindow();
const template = [
{
label: "File",
submenu: [{
label: 'About',
click: _ => {
console.log('clicked');
}
},
{
type: 'separator'
},
{
label: 'Quit',
accelerator: 'Alt+W',
click: _ => {
app.quit();
}
}]
}];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
});
The accelerator String is no longer supported. The documentation was updated in v1.4.5 to clarify how to define shortcuts using globalShortcut.
Accelerator Documentation: Shortcuts are registered with the globalShortcut module using the register method, i.e.
const {app, globalShortcut} = require('electron')
app.on('ready', () => {
// Register a 'CommandOrControl+Y' shortcut listener.
globalShortcut.register('CommandOrControl+Y', () => {
// Do stuff when Y and either Command/Control is pressed.
})
})
So change your code to this
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const Menu = electron.Menu;
const globalShortcut = electron.globalShortcut;
app.on('ready', _ => {
new BrowserWindow();
// Declare shortcuts
globalShortcut.register('Alt+W', () => app.quit());
const template = [
{
label: "File",
submenu: [{
label: 'About',
click: _ => {
console.log('clicked');
}
},
{
type: 'separator'
},
{
label: 'Quit',
click: _ => {
app.quit();
}
}]
}];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
});
looks like on Windows it needs something loaded on the BrowserWindow. Placing the loadURL make it work. Need to try something besides loadURL though..
app.on('ready', _ => {
mainWindow = new BrowserWindow()
mainWindow.loadURL('https://github.com')
const name = electron.app.getName()
const template = [
{
label: name,
submenu: [{
label: `About ${name}` ,
click: console.log('clicked!')
},{
type:'separator'
},{
label:'Quit',
click: _ =>{
app.quit()
},
accelerator:'CmdOrCtrl+Q'
}
]
}
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
})

Resources