Merge "Do not crash on missing root for flamegraph."
diff --git a/src/profiling/memory/CHANGELOG.md b/src/profiling/memory/CHANGELOG.md
index 33f1cde..c4155b5 100644
--- a/src/profiling/memory/CHANGELOG.md
+++ b/src/profiling/memory/CHANGELOG.md
@@ -7,6 +7,8 @@
 
 ## Bugfixes
 * Fix problems with allocations done in signal handlers using SA_ONSTACK.
+* Fixed heapprofd for multi API. A 64-bit heapprofd service can now correctly
+  profile a 32-bit target.
 
 # In Android 11
 
diff --git a/ui/package-lock.json b/ui/package-lock.json
index f325318..8613d3f 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -5134,9 +5134,9 @@
       }
     },
     "mithril": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/mithril/-/mithril-2.0.4.tgz",
-      "integrity": "sha512-mgw+DMZlhMS4PpprF6dl7ZoeZq5GGcAuWnrg5e12MvaGauc4jzWsDZtVGRCktsiQczOEUr2K5teKbE5k44RlOg=="
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/mithril/-/mithril-1.1.7.tgz",
+      "integrity": "sha512-1SAkGeVrIVvkUHlPHvR3pXdWzNfTzmS/fBAe+rC2ApEBfZFFc+idi8Qg/M5JoW/sZkIDXSfQYVgvENMIhBIVAg=="
     },
     "mixin-deep": {
       "version": "1.3.2",
diff --git a/ui/package.json b/ui/package.json
index 0262dd5..22573c1 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -11,7 +11,7 @@
     "@types/chrome": "0.0.86",
     "@types/color-convert": "^1.9.0",
     "@types/gtag.js": "0.0.3",
-    "@types/mithril": "^2.0.3",
+    "@types/mithril": "^1.1.17",
     "@types/node": "^14.0.10",
     "@types/pako": "^1.0.1",
     "@types/uuid": "^3.4.9",
@@ -23,7 +23,7 @@
     "events": "^3.1.0",
     "immer": "^1.12.1",
     "micromodal": "^0.4.6",
-    "mithril": "^2.0.3",
+    "mithril": "^1.1.7",
     "noice-json-rpc": "^1.2.0",
     "pako": "^1.0.11",
     "protobufjs": "^6.9.0",
diff --git a/ui/src/frontend/error_dialog.ts b/ui/src/frontend/error_dialog.ts
index 211d6e5..1c44b98 100644
--- a/ui/src/frontend/error_dialog.ts
+++ b/ui/src/frontend/error_dialog.ts
@@ -85,20 +85,25 @@
     shareTraceSection.push(
         m(`input[type=checkbox]`, {
           checked,
-          oninput: (ev: InputEvent) => {
-            checked = (ev.target as HTMLInputElement).checked;
-            if (checked && engine.source.type === 'FILE') {
-              saveTrace(engine.source.file).then(url => {
-                const errMessage = createErrorMessage(errLog, checked, url);
+          oninput: m.withAttr(
+              'checked',
+              value => {
+                checked = value;
+                if (value && engine.source.type === 'FILE') {
+                  saveTrace(engine.source.file).then((url) => {
+                    const errMessage = createErrorMessage(errLog, checked, url);
+                    renderModal(
+                        errTitle,
+                        errMessage,
+                        userDescription,
+                        shareTraceSection);
+                    return;
+                  });
+                }
+                const errMessage = createErrorMessage(errLog, checked);
                 renderModal(
                     errTitle, errMessage, userDescription, shareTraceSection);
-                return;
-              });
-            }
-            const errMessage = createErrorMessage(errLog, checked);
-            renderModal(
-                errTitle, errMessage, userDescription, shareTraceSection);
-          },
+              })
         }),
         m('span', `Check this box to share the current trace for debugging 
      purposes.`),
@@ -129,9 +134,11 @@
           m('textarea.modal-textarea', {
             rows: 3,
             maxlength: 1000,
-            oninput: (ev: InputEvent) => {
-              userDescription = (ev.target as HTMLTextAreaElement).value;
-            },
+            oninput: m.withAttr(
+                'value',
+                v => {
+                  userDescription = v;
+                })
           }),
           shareTraceSection),
     buttons: [
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index d414162..7ff1840 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -318,11 +318,11 @@
   // /?s=xxxx for permalinks.
   const stateHash = Router.param('s');
   const urlHash = Router.param('url');
-  if (typeof stateHash === 'string' && stateHash) {
+  if (stateHash) {
     globals.dispatch(Actions.loadPermalink({
       hash: stateHash,
     }));
-  } else if (typeof urlHash === 'string' && urlHash) {
+  } else if (urlHash) {
     globals.dispatch(Actions.openTraceFromUrl({
       url: urlHash,
     }));
diff --git a/ui/src/frontend/notes_panel.ts b/ui/src/frontend/notes_panel.ts
index 4e1365d..0aae5a9 100644
--- a/ui/src/frontend/notes_panel.ts
+++ b/ui/src/frontend/notes_panel.ts
@@ -273,23 +273,25 @@
               e.stopImmediatePropagation();
             },
             value: note.text,
-            onchange: (e: InputEvent) => {
-              const newText = (e.target as HTMLInputElement).value;
-              globals.dispatch(Actions.changeNoteText({
-                id: attrs.id,
-                newText,
-              }));
-            },
+            onchange: m.withAttr(
+                'value',
+                newText => {
+                  globals.dispatch(Actions.changeNoteText({
+                    id: attrs.id,
+                    newText,
+                  }));
+                }),
           }),
           m('span.color-change', `Change color: `, m('input[type=color]', {
               value: note.color,
-              onchange: (e: Event) => {
-                const newColor = (e.target as HTMLInputElement).value;
-                globals.dispatch(Actions.changeNoteColor({
-                  id: attrs.id,
-                  newColor,
-                }));
-              },
+              onchange: m.withAttr(
+                  'value',
+                  newColor => {
+                    globals.dispatch(Actions.changeNoteColor({
+                      id: attrs.id,
+                      newColor,
+                    }));
+                  }),
             })),
           m('button',
             {
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
index b6683f3..62f728b 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/frontend/record_page.ts
@@ -121,14 +121,15 @@
   const recButton = (mode: RecordMode, title: string, img: string) => {
     const checkboxArgs = {
       checked: cfg.mode === mode,
-      onchange: (e: InputEvent) => {
-        const checked = (e.target as HTMLInputElement).checked;
-        if (!checked) return;
-        const traceCfg = produce(globals.state.recordConfig, draft => {
-          draft.mode = mode;
-        });
-        globals.dispatch(Actions.setRecordConfig({config: traceCfg}));
-      },
+      onchange: m.withAttr(
+          'checked',
+          (checked: boolean) => {
+            if (!checked) return;
+            const traceCfg = produce(globals.state.recordConfig, draft => {
+              draft.mode = mode;
+            });
+            globals.dispatch(Actions.setRecordConfig({config: traceCfg}));
+          })
     };
     return m(
         `label${cfg.mode === mode ? '.selected' : ''}`,
@@ -800,9 +801,7 @@
           m('select',
             {
               selectedIndex,
-              onchange: (e: Event) => {
-                onTargetChange((e.target as HTMLSelectElement).value);
-              },
+              onchange: m.withAttr('value', onTargetChange),
               onupdate: (select) => {
                 // Work around mithril bug
                 // (https://github.com/MithrilJS/mithril.js/issues/2107): We may
@@ -1312,8 +1311,7 @@
     };
 
     const pages: m.Children = [];
-    const routePageParam = Router.param('p');
-    let routePage = typeof routePageParam === 'string' ? routePageParam : '';
+    let routePage = Router.param('p');
     if (!Object.keys(SECTIONS).includes(routePage)) {
       routePage = 'buffers';
     }
diff --git a/ui/src/frontend/record_widgets.ts b/ui/src/frontend/record_widgets.ts
index 9558919..4d55409 100644
--- a/ui/src/frontend/record_widgets.ts
+++ b/ui/src/frontend/record_widgets.ts
@@ -74,12 +74,8 @@
           onclick: () => onToggle(!enabled),
         }),
         m('label',
-          m(`input[type=checkbox]`, {
-            checked: enabled,
-            oninput: (e: InputEvent) => {
-              onToggle((e.target as HTMLInputElement).checked);
-            },
-          }),
+          m(`input[type=checkbox]`,
+            {checked: enabled, oninput: m.withAttr('checked', onToggle)}),
           m('span', attrs.title)),
         m('div', m('div', attrs.descr), m('.probe-config', children)));
   }
@@ -144,17 +140,13 @@
         type: 'text',
         pattern: '(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]){2}',  // hh:mm:ss
         value: new Date(val).toISOString().substr(11, 8),
-        oninput: (e: InputEvent) => {
-          this.onTimeValueChange(attrs, (e.target as HTMLInputElement).value);
-        },
+        oninput: m.withAttr('value', v => this.onTimeValueChange(attrs, v))
       };
     } else {
       spinnerCfg = {
         type: 'number',
         value: val,
-        oninput: (e: InputEvent) => {
-          this.onTimeValueChange(attrs, (e.target as HTMLInputElement).value);
-        },
+        oninput: m.withAttr('value', v => this.onValueChange(attrs, v))
       };
     }
     return m(
@@ -164,11 +156,7 @@
         attrs.icon !== undefined ? m('i.material-icons', attrs.icon) : [],
         m(`input[id="${id}"][type=range][min=0][max=${maxIdx}][value=${idx}]
         ${disabled ? '[disabled]' : ''}`,
-          {
-            oninput: (e: InputEvent) => {
-              this.onSliderChange(attrs, +(e.target as HTMLInputElement).value);
-            },
-          }),
+          {oninput: m.withAttr('value', v => this.onSliderChange(attrs, v))}),
         m(`input.spinner[min=${min !== undefined ? min : 1}][for=${id}]`,
           spinnerCfg),
         m('.unit', attrs.unit));
diff --git a/ui/src/frontend/topbar.ts b/ui/src/frontend/topbar.ts
index 1bca200..e8403c3 100644
--- a/ui/src/frontend/topbar.ts
+++ b/ui/src/frontend/topbar.ts
@@ -110,16 +110,17 @@
         `.omnibox${commandMode ? '.command-mode' : ''}`,
         m('input', {
           placeholder: PLACEHOLDER[mode],
-          oninput: (e: InputEvent) => {
-            const value = (e.target as HTMLInputElement).value;
-            globals.frontendLocalState.setOmnibox(
-                value, commandMode ? 'COMMAND' : 'SEARCH');
-            if (mode === SEARCH) {
-              globals.frontendLocalState.setSearchIndex(-1);
-              displayStepThrough = value.length >= 4;
-              globals.rafScheduler.scheduleFullRedraw();
-            }
-          },
+          oninput: m.withAttr(
+              'value',
+              v => {
+                globals.frontendLocalState.setOmnibox(
+                    v, commandMode ? 'COMMAND' : 'SEARCH');
+                if (mode === SEARCH) {
+                  globals.frontendLocalState.setSearchIndex(-1);
+                  displayStepThrough = v.length >= 4;
+                  globals.rafScheduler.scheduleFullRedraw();
+                }
+              }),
           value: globals.frontendLocalState.omnibox,
         }),
         displayStepThrough ?