graalvm-mobile

https://github.com/phronmophobic/mobiletest
borkdude 2021-06-25T08:34:46.150Z

@smith.adriane @raspasov It's worth trying to not use --initialize-at-build-time but using this approach: https://github.com/oracle/graal/discussions/3476#discussioncomment-897705 to see if this helps.

👍 1
phronmophobic 2021-06-25T17:06:32.151700Z

I'm trying it out. Just blindly following the example produces the following errors:

To see how the classes got initialized, use --trace-class-initialization=primitive_math$variadic_proxy,primitive_math$using_primitive_operators_QMARK_,primitive_math$use_primitive_operators,primitive_math$variadic_predicate_proxy,primitive_math$unuse_primitive_operators
[mobiletest-uber:95490]     analysis:  40,021.43 ms,  3.22 GB
Error: Classes that should be initialized at run time got initialized during image building:
 primitive_math$variadic_proxy was unintentionally initialized at build time. To see why primitive_math$variadic_proxy got initialized use --trace-class-initialization=primitive_math$variadic_proxy
primitive_math$using_primitive_operators_QMARK_ was unintentionally initialized at build time. To see why primitive_math$using_primitive_operators_QMARK_ got initialized use --trace-class-initialization=primitive_math$using_primitive_operators_QMARK_
primitive_math$use_primitive_operators was unintentionally initialized at build time. To see why primitive_math$use_primitive_operators got initialized use --trace-class-initialization=primitive_math$use_primitive_operators
primitive_math$variadic_predicate_proxy was unintentionally initialized at build time. To see why primitive_math$variadic_predicate_proxy got initialized use --trace-class-initialization=primitive_math$variadic_predicate_proxy
primitive_math$unuse_primitive_operators was unintentionally initialized at build time. To see why primitive_math$unuse_primitive_operators got initialized use --trace-class-initialization=primitive_math$unuse_primitive_operators

com.oracle.svm.core.util.UserError$UserException: Classes that should be initialized at run time got initialized during image building:
 primitive_math$variadic_proxy was unintentionally initialized at build time. To see why primitive_math$variadic_proxy got initialized use --trace-class-initialization=primitive_math$variadic_proxy
primitive_math$using_primitive_operators_QMARK_ was unintentionally initialized at build time. To see why primitive_math$using_primitive_operators_QMARK_ got initialized use --trace-class-initialization=primitive_math$using_primitive_operators_QMARK_
primitive_math$use_primitive_operators was unintentionally initialized at build time. To see why primitive_math$use_primitive_operators got initialized use --trace-class-initialization=primitive_math$use_primitive_operators
primitive_math$variadic_predicate_proxy was unintentionally initialized at build time. To see why primitive_math$variadic_predicate_proxy got initialized use --trace-class-initialization=primitive_math$variadic_predicate_proxy
primitive_math$unuse_primitive_operators was unintentionally initialized at build time. To see why primitive_math$unuse_primitive_operators got initialized use --trace-class-initialization=primitive_math$unuse_primitive_operators
I'll try to use --trace-class-initialization and see if I can get a working list

borkdude 2021-06-25T17:23:15.152300Z

@smith.adriane you'll need to use the namespace list of your specific application, generated using the sniippet I provided

borkdude 2021-06-25T17:23:23.152500Z

were you doing that?

phronmophobic 2021-06-25T17:23:27.152700Z

yea

borkdude 2021-06-25T17:23:46.153300Z

it seems you'll need to add primitive_math

borkdude 2021-06-25T17:24:04.153600Z

perhaps the snippet didn't account for some characters

phronmophobic 2021-06-25T17:24:22.153800Z

just noticed that your list starts with "clojure", but it doesn't show up in mine

borkdude 2021-06-25T17:24:44.154200Z

I did a (cons "clojure" ..) somewhere manually

borkdude 2021-06-25T17:25:08.154500Z

(->> (map ns-name (all-ns)) (remove #(str/starts-with? % "clojure")) (map #(str/split (str %) #"\.")) (keep butlast) (map #(str/join "." %)) distinct (map munge) (cons "clojure"))
see the end

borkdude 2021-06-25T17:26:14.154700Z

you'll first need to require your main namespace

phronmophobic 2021-06-25T17:27:28.155200Z

whoops. I think just forgot how to read for a second

phronmophobic 2021-06-25T17:27:36.155500Z

trying with "primitive_math" included

borkdude 2021-06-25T17:28:25.156100Z

execute the above in a REPL, but first do:

(require 'your.main)
(require '[clojure.string :as str])

phronmophobic 2021-06-25T17:29:47.157100Z

I added the following function:

(defn initialize-at-build-time-list [& args]
  (println
   (->> (map ns-name (all-ns))
        (remove #(clojure.string/starts-with? % "clojure"))
        (map #(clojure.string/split (str %) #"\."))
        (keep butlast)
        (map #(clojure.string/join "." %))
        distinct
        (map munge)
        (cons "clojure")
        (clojure.string/join ","))))
so I can have it referenced in my compile script:
INITIALIZE_AT_BUILD_TIME=`clojure -X com.phronmophobic.mobiletest/initialize-at-build-time-list
` ...
--initialize-at-build-time="$INITIALIZE_AT_BUILD_TIME" \

borkdude 2021-06-25T17:30:18.157500Z

yeah cool. can you also echo what's in the var?

borkdude 2021-06-25T17:30:37.157900Z

env var

phronmophobic 2021-06-25T17:30:41.158Z

set -x will print it as its executed

borkdude 2021-06-25T17:30:51.158300Z

I mean, can you list it here?

phronmophobic 2021-06-25T17:30:54.158500Z

+ INITIALIZE_AT_BUILD_TIME=clojure,tech.v3.datatype,sci.impl,babashka.nrepl,tech.v3.resource,bencode,flatland.ordered,com.phronmophobic,babashka.nrepl.impl,datascript,edamame.impl,tech.v3,tech.v3.parallel,sci.addons,tech.v3.datatype.ffi,sci,primitive_math

borkdude 2021-06-25T17:31:22.158800Z

that seems good, but I don't see any of your namespaces...

borkdude 2021-06-25T17:31:24.159Z

right?

phronmophobic 2021-06-25T17:31:32.159200Z

com.phronmophobic

borkdude 2021-06-25T17:31:36.159400Z

ah right

borkdude 2021-06-25T17:31:45.159700Z

ok, so this should do it then... hopefully

phronmophobic 2021-06-25T17:32:11.160Z

primitive_math$variadic_predicate_proxy was unintentionally initialized at build time. com.phronmophobic.mobiletest caused initialization of this class with the following trace: 
	at primitive_math$variadic_predicate_proxy.<clinit>(primitive_math.clj:27)

phronmophobic 2021-06-25T17:32:37.160200Z

does order matter?

borkdude 2021-06-25T17:32:46.160500Z

no. what is primitive_math?

borkdude 2021-06-25T17:33:02.161Z

I think the issue here may be that it's not in a package, just a bare class name

phronmophobic 2021-06-25T17:33:15.161300Z

It's used by dtype-next

borkdude 2021-06-25T17:33:21.161500Z

this is why you should probably never use a single segment namespace

phronmophobic 2021-06-25T17:34:10.162300Z

wasn't me! 😛

borkdude 2021-06-25T17:35:11.162700Z

I'll take this up with graalvm devs

borkdude 2021-06-25T17:35:38.163200Z

you'll probably run into lots of classes from this library that you should explicitly list...

borkdude 2021-06-25T17:35:45.163400Z

because it has no package

phronmophobic 2021-06-25T17:36:35.163800Z

is it fixable without graalvm changes?

phronmophobic 2021-06-25T17:37:09.164600Z

just by adding primitive_math$variadic_predicate_proxy and whatever else it complains about?

borkdude 2021-06-25T17:38:43.164800Z

yes

borkdude 2021-06-25T17:38:59.165200Z

but you'll likely need to enumerate quite few of them..

borkdude 2021-06-25T17:40:58.165800Z

you could perhaps do this using the list of class files in the classes/primitive_math directory or so

phronmophobic 2021-06-25T17:41:28.166300Z

I've added all the classes that were printed

phronmophobic 2021-06-25T17:41:32.166500Z

compiling now

borkdude 2021-06-25T17:41:40.166900Z

it will likely come up with a new class

phronmophobic 2021-06-25T17:41:53.167200Z

if only they returned the error as edn 😛 (or even json).

phronmophobic 2021-06-25T17:43:36.168Z

{:unintentially-initialized ["primitive_math$variadic_predicate_proxy" ...]
 :error "Classes that should be initialized at run time got initialized during image building"}

borkdude 2021-06-25T17:44:09.168400Z

escape the dollar?

phronmophobic 2021-06-25T17:44:47.169Z

the snippet was a wishful example of native image output

phronmophobic 2021-06-25T17:44:50.169200Z

it seems like it's compiling

borkdude 2021-06-25T17:44:53.169500Z

right

phronmophobic 2021-06-25T17:44:56.169600Z

it compiled!

borkdude 2021-06-25T17:46:05.169800Z

🎉

phronmophobic 2021-06-25T17:47:32.170700Z

rather than using:

(defn initialize-at-build-time-list [& args]
  (println
   (->> (map ns-name (all-ns))
        (remove #(clojure.string/starts-with? % "clojure"))
        (map #(clojure.string/split (str %) #"\."))
        (keep butlast)
        (map #(clojure.string/join "." %))
        distinct
        (map munge)
        (cons "clojure")
        (clojure.string/join ","))))
It seems like it would be more robust to just examine the classes in the uberjar. Would that work?

borkdude 2021-06-25T17:48:46.171200Z

@smith.adriane well, the point in the linked discussion is that not all classes should be initialized at build time, only the clojure ones

borkdude 2021-06-25T17:49:18.171800Z

so if there are other Java classes in your jar then those should not be listed preferably

borkdude 2021-06-25T17:49:29.172100Z

since native-image should figure it out by itself

phronmophobic 2021-06-25T17:49:45.172300Z

oh, interesting

borkdude 2021-06-25T17:50:27.172700Z

and the deadlock can happen because of initializing at build time for some of the Java stuff I think

borkdude 2021-06-25T17:51:03.173300Z

but it remains to be seen if the above approach has really solved the issue. did you push the change? I could try it locally

phronmophobic 2021-06-25T17:51:33.173500Z

Will do. one sec

borkdude 2021-06-25T17:53:14.173800Z

I will also try this approach for https://github.com/borkdude/tools-deps-native-experiment which has a similar issue

phronmophobic 2021-06-25T17:55:43.174200Z

ok, pushed the changes

phronmophobic 2021-06-25T17:57:05.175200Z

It seems like it should be possible to generate the list of clojure classes since projects like depstar need to produce a list for AOT compilation

borkdude 2021-06-25T17:58:23.175400Z

good thinking

phronmophobic 2021-06-25T19:13:29.177800Z

Using the package for initialize at build time can have false positive since it's possible to have a java class that shares the same package as clojure code. I tried filtering all classnames in an uber jar by matching them with namespaces:

(defn list-resources [path]
  (let [jar (java.util.jar.JarFile. path)  
        entries (.entries jar)]
    (loop [result  []]
      (if (.hasMoreElements entries)
        (recur (conj result (.. entries nextElement getName)))
        result))))

(defn namespace->namespace-key [ns]
  (-> ns
      ns-name
      str
      (clojure.string/split #"\.")
      (->> (map munge))))

(defn class-path->namespace-key [fname]
  (-> fname
      (clojure.string/replace #"(\$[^.]+)?.class$" "")
      (clojure.string/split #"/"))
  )

(defn class-path->classname [fname]
  (-> (subs fname 0 (- (count fname)
                       (count ".class")))
      (clojure.string/replace #"/" ".")))

(defn clojure-classes-in-jar [jar-path]
  (let [resources (list-resources jar-path)
        ns-keys (into
                 #{}
                 (map namespace->namespace-key)
                 (all-ns))
        classes (into #{}
                      (comp
                       (filter #(clojure.string/ends-with? % ".class"))
                       (filter (fn [class-path]
                                 (contains? ns-keys (class-path->namespace-key class-path))))
                       (map class-path->classname))
                      resources)]
    classes))

(comment
  (spit "initialize-at-buildtime-classes.txt"
        (clojure.string/join ","
                             (clojure-classes-in-jar  "target/mobiletest-uber.jar")))

  ;; INITIALIZE_AT_BUILD_TIME=$(cat initialize-at-buildtime-classes.txt)
  ;;     --initialize-at-build-time=clojure,"$INITIALIZE_AT_BUILD_TIME" \
)
This doesn't quite work because it doesn't include classes created by defrecord. It's also a comically large list.

phronmophobic 2021-06-25T19:15:46.179100Z

However it should be possible to modify the above code to include defrecord classes if including java classes that share packages with clojure namespaces isn't a big deal

borkdude 2021-06-25T19:22:18.179400Z

> if including java classes that share packages with clojure namespaces isn't a big deal sometimes it is, in the case of httpkit for example

borkdude 2021-06-25T19:22:22.179600Z

see its README

borkdude 2021-06-25T19:22:33.179900Z

but that would also be the case with the namespace approach

phronmophobic 2021-06-25T19:24:35.181300Z

I guess it should also be possible to list all classes created by defrecord and include them when filtering classes in an uberjar