1 #Region "Microsoft.VisualBasic::fbae67b2a610cf873dc16ac6a28a5169, Microsoft.VisualBasic.Core\ApplicationServices\VBDev\Ngen\Ngen.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Module NgenInstaller
35     
36     
37     '         Enum Scenarios
38     
39     
40     
41     
42     '         Enum PriorityLevels
43     
44     '             Idle
45     
46     '  
47     
48     
49     
50     '         Enum QueueActions
51     
52     
53     '  
54     
55     
56     
57     '         Enum InstallTypes
58     
59     '             Install, Uninstall
60     
61     '  
62     
63     
64     
65     '  
66     
67     '     Properties: Ngen
68     
69     '     Sub: NgenFile
70     
71     
72     ' /********************************************************************************/
73
74 #End Region
75
76 Imports System.ComponentModel
77 Imports System.Runtime.InteropServices
78 Imports System.Text
79 Imports Microsoft.VisualBasic.ApplicationServices
80 Imports Microsoft.VisualBasic.CommandLine
81 Imports Microsoft.VisualBasic.CommandLine.Reflection
82 Imports Microsoft.VisualBasic.Language
83 Imports Microsoft.VisualBasic.Language.UnixBash
84
85 Namespace ApplicationServices
86
87     ''' <summary>
88     ''' Ngen.exe (Native Image Generator)
89     ''' 
90     ''' The Native Image Generator (Ngen.exe) is a tool that improves the performance of managed applications. 
91     ''' Ngen.exe creates native images, which are files containing compiled processor-specific machine code, 
92     ''' and installs them into the native image cache on the local computer. The runtime can use native images 
93     ''' from the cache instead of using the just-in-time (JIT) compiler to compile the original assembly.
94     ''' 
95     ''' Changes To Ngen.exe In the .NET Framework 4
96     ''' Ngen.exe now compiles assemblies With full trust, And code access security (CAS) policy Is no longer evaluated.
97     ''' Native images that are generated With Ngen.exe can no longer be loaded into applications that are running In Partial trust.
98     ''' 
99     ''' Changes To Ngen.exe In the .NET Framework version 2.0:
100     ''' Installing an assembly also installs its dependencies, simplifying the syntax Of Ngen.exe.
101     ''' Native images can now be Shared across application domains.
102     ''' New Action, update, re - creates images that have been invalidated.
103     ''' Actions can be deferred For execution by a service that uses idle time On the computer To generate And install images.
104     ''' Some causes Of image invalidation have been eliminated.
105     ''' </summary>
106     ''' <remarks>
107     ''' 1.7 Native代码产生器: NGen.exe
108     ''' 
109     ''' 随.NET Framework发布的NGen.exe工具可以将IL代码编译成native代码, 当应用程序安装在用户的机器上时. 因为代码是在安装的时候编译的, CLR的JIT编译器不需要在运行时刻编译IL代码
110     ''' 这能提高应用程序的性能. NGen.exe工具在下面两个场合很有趣:
111     ''' 
112     ''' 提高了应用程序的启动速度 运行NGen.exe能提高启动速度, 因为代码已经编译成native代码, 所以在运行时就不需要编译了
113     ''' 减少应用程序的工作集 如果你认为一个程序集会被同时载入到多个进程/ Appdomain, 在这个程序集上运行NGen.exe能减少应用程序的工作集, 其原因是NGen.exe工具将IL编译成native代码,
114     ''' 然后将输出保存到单独的文件中, 这个文件能同时被内存映射(memory - mapping)到多个进程地址空间中, 允许代码共享, 每个进程 / AppDomain不必为自己拷贝一份代码
115     ''' 
116     ''' 当一个安装程序调用NGen.exe对一个应用程序或程序集进行编译时, 那个应用程序的所有程序集或者一个特定的程序集会把其IL代码编译成native代码, 一个新的只包含native代码而不含有IL的程序集文件会被NGen.exe创建.
117     ''' 这个新的文件被放到名字类似于 C: /Windows/Assembly/NativeImages_v2.0.50727_32的文件夹下面, 这个文件家名字包含了CLR的版本和native代码是否是为x86(32位版本的Windows), x64, 
118     ''' 或者Itaninum(64位版本的Windows)编译的信息.
119     ''' 
120     ''' 现在, 当CLR载入一个程序集文件时, CLR查看对应的NGen native文件是否存在, 如果没发现native文件, CLR JIT对IL代码像通常那样进行编译.
121     ''' 然而, 如果对应的native文件存在, CLR将使用native文件中的编译好的代码, 文件中的函数就不需要在运行时刻编译了.
122     ''' 
123     ''' 在表面上, 这听起来非常好, 听上去就像如果你得到了托管代码的全部优点(垃圾回收, 代码验证, 类型安全, 等等)而不牺牲托管代码的性能(JIT编译), 
124     ''' 但是实际情况并不总是那么美好, NGen'd文件有几个潜在的问题:
125     ''' 
126     ''' 没有知识产权保护 很多人以为可以发布NGen文件而不用发布包含原始IL代码的文件, 从而使他们的知识产权更加保密
127     ''' 不幸的是, 这并不可行, 在运行时刻, CLR需要访问程序集的metadata(为某些函数, 例如反射和串行化函数), 这需要发布包含IL和metadata的程序集.
128     ''' 此外, 如果由于某种原因, CLR不能使用NGen文件(如下面所描述的), 那么CLR会回到JIT编译, 对程序集的IL代码进行编译, 因此IL代码必须存在.
129     ''' 
130     ''' NGen文件可能会过时 当CLR载入NGen文件时, 它会比较以前编译的代码和当前的执行环境的很多特征, 如果任何特征不匹配, NGen文件就不能被使用, JIT编译器进程就要使用. 这是必须被匹配的部分特征列表.
131     ''' 
132     '''   程序集模块的版本ID(MVID)
133     '''   被引用的程序集的版本ID
134     '''   处理器类型
135     '''   CLR版本
136     '''   Build类型(release, debug, optimized debug, profiling, 等等)
137     ''' 
138     ''' 所有链接时的安全性要求都必须在运行时刻被满足才能允许载入.
139     ''' 
140     ''' 注意有可能以升级的方式运行NGen.exe, 这告诉工具对以前曾经被执行NGen'd的所有的程序集上运行NGen.exe. 当终端用户安装.NET Framework的一个新service pack, 
141     ''' 那么service pack的安装程序将会在更新模式下自动运行NGen.exe, 使得NGen文件保持和CLR的版本一致.
142     ''' 
143     ''' 较差的载入时性能(重定位/绑定): 程序集文件是标准的Windows PE文件, 每个文件包含着一个优先使用的基地址. 很多Windows开发者对围绕基地址和重定位的问题很熟悉, 
144     ''' 关于这个主题的更多信息, 可以参考我的书 programming Applications for Microsoft Windows, 4th Edition. 当JIT编译代码时, 不必关心这些问题, 因为正确的内存地址引用会在运行时计算出来.
145     ''' 
146     ''' 然而, NGen的程序集文件的一些内存地址引用是静态计算的, 当Windows加载一个NGen文件时, 它检查文件是否被载入到优先的基地址上, 如果文件没有载入到优先的基地址, 
147     ''' Windows会重新定位文件, 修改所有内存地址引用. 这是极其耗时的, 因为Windows必须载入整个文件, 并修改文件中的很多字节. 此外, 这个页面文件对应的代码不能跨进程边界共享.
148     ''' 
149     ''' 因此如果你打算NGen程序集文件, 你应该为你的程序集文件选择好的基地址(通过csc.exe的 / baseaddress命令行开关).当你NGen一个程序集文件时, NGen文件将被赋予一个基地址, 
150     ''' 这需要使用一个基于托管程序集基地址的算法. 不幸的是, 微软从没有一个良好的指导来帮助开发者如何赋予基地址. 在64位版本的Windows上, 这还不太会成为问题, 因为地址空间是很充足的, 
151     ''' 但是对于一个32位的地址空间, 为每一个程序集选择一个好的基地址几乎是不可能的, 除非你精确地知道什么东西会被载入到进程, 知道那个程序集的大小不会超过后一个版本.
152     ''' 
153     ''' 较差的执行时性能 当编译代码时, NGen对执行环境做出的假设不会比JIT编译器的多, 这会造成NGen.exe产生较差的代码, 例如, NGen不能优化一些CPU指令, 对静态字段的访问需要简介的操作,
154     ''' 因为静态字段实际的地址需要在运行时刻才能知道.NGen到处插入代码来调用类的构造函数, 因为它不知道代码执行的次序, 不知道类的构造憾事是否已经被调用了(见第8章, 类的构造函数).
155     ''' 一些NGen应用程序会比JIT编译的代码慢大约5%, 因此, 如果你打算使用NGen来提高应用程序的性能, 你应该对比NGen’d和非NGen’d版本的应用程序, 确定NGen’d版本在实际执行时并不慢. 
156     ''' 对于一些应用程序, 减小的工作集大小会提高性能, 因此NGen总体上还是会取胜.
157     ''' 
158     ''' 因为上面列出的所有问题, 当考虑使用NGen.exe时, 你应该非常小心.对于服务器端的应用程序来说, NGen.exe的用处很小甚至没有意义, 因为只有第一个客户需求经历了性能上的下降, 
159     ''' 后面的客户需求都是高速运行的.此外, 对于大多数服务器应用程序, 只需要代码的一个实例, 因此没有工作集方面的利益.
160     ''' 
161     ''' 对于客户端应用程序, NGen.exe可能对于提高启动速度或者减小工作集有帮助, 如果程序集被多个应用程序同时使用.甚至没有多个应用程序使用一个程序集, NGen一个程序集也会提高工作集.
162     ''' 此外, 如果NGen.exe被用于所有的客户端应用程序的程序集, 那么CLR就根本不需要载入JIT编译器, 从而更进一步地降低了工作集.
163     ''' 当然, 如果只有一个程序集不是NGen'd或者如果一个程序集的NGen文件不能被使用, JIT编译器就会被载入, 应用程序的工作集将会增加.
164     ''' </remarks>
165     <RunInstaller(True)>
166     <[Namespace]("Ngen")>
167     Public Module NgenInstaller
168
169 #Region "Actions::The following table shows the syntax Of Each action."
170
171         Public Enum Scenarios
172             ''' <summary>
173             ''' Generate native images that can be used under a debugger.
174             ''' </summary>
175             <Description("/Debug")> Debug
176             ''' <summary>
177             ''' Generate native images that can be used under a profiler.
178             ''' </summary>
179             <Description("/Profile")> Profile
180             ''' <summary>
181             ''' Generate the minimum number Of native images required by the specified scenario options.
182             ''' </summary>
183             <Description("/NoDependencies")> NoDependencies
184         End Enum
185
186         Public Enum PriorityLevels
187             null = -1
188
189             ''' <summary>
190             ''' 1 Native images are generated And installed immediately, without waiting For idle time.
191             ''' </summary>
192             Immediately = 1
193             ''' <summary>
194             ''' 2 Native images are generated And installed without waiting For idle time, but after all priority 1 actions (And their dependencies) have completed.
195             ''' </summary>
196             Waiting = 2
197             ''' <summary>
198             ''' 3 Native images are installed When the native image service detects that the computer Is idle. See Native Image Service.
199             ''' </summary>
200             Idle
201         End Enum
202
203         ''' <summary>
204         ''' Generate native images for an assembly and its dependencies and install the images in the native image cache.
205         ''' </summary>
206         ''' <param name="assemblyName">
207         ''' The full display name of the assembly. For example, "myAssembly, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0038abc9deabfle5".
208         ''' Only one assembly can be specified per Ngen.exe command line.
209         ''' 
210         ''' Note You can supply a Partial assembly name, such As myAssembly, For the display And uninstall actions.
211         ''' 
212         ''' The explicit path of the assembly. You can specify a full or relative path.
213         ''' If you specify a file name without a path, the assembly must be located In the current directory.
214         ''' Only one assembly can be specified per Ngen.exe command line.
215         ''' </param>
216         ''' <param name="scenarios"></param>
217         ''' <param name="ExeConfig">exePath, Use the configuration of the specified executable assembly.
218         ''' Ngen.exe needs to make the same decisions as the loader when binding to dependencies. When a shared component Is loaded at run time, 
219         ''' using the Load method, the application's configuration file determines the dependencies that are loaded for the shared component — 
220         ''' for example, the version of a dependency that is loaded. The /ExeConfig switch gives Ngen.exe guidance on which dependencies would be loaded at run time.</param>
221         ''' <param name="AppBase">directoryPath, When locating dependencies, use the specified directory as the application base.</param>
222         ''' <param name="queue">If /queue is specified, the action is queued for the native image service. The default priority is 3. See the Priority Levels table.</param>
223         Public Function Install(assemblyName$,
224                                 scenarios As NgenInstaller.Scenarios,
225                                 Optional ExeConfig$ = "",
226                                 Optional AppBase As Boolean = False,
227                                 Optional queue As NgenInstaller.PriorityLevels = PriorityLevels.null) As String
228
229             Dim cliBuilder As New StringBuilder("install ", 1024)
230
231             Call cliBuilder.Append(assemblyName.CLIPath & " ")
232             Call cliBuilder.Append(scenarios.Description & " ")
233
234             If Not String.IsNullOrEmpty(ExeConfig) Then
235                 Call cliBuilder.Append($"/ExeConfig:{ExeConfig}".CLIPath & " ")
236             End If
237
238             If AppBase Then
239                 Call cliBuilder.Append("/AppBase ")
240             End If
241
242             If Not queue = PriorityLevels.null Then
243                 Call cliBuilder.Append($"/queue:{CInt(queue)}")
244             End If
245
246             Dim NGen As New IORedirectFile(NgenInstaller.Ngen, cliBuilder.ToString & " /verbose")
247             Call NGen.Run()
248             Return NGen.StandardOutput
249         End Function
250
251         ''' <summary>
252         ''' Delete the native images of an assembly and its dependencies from the native image cache.
253         ''' To uninstall a single image And its dependencies, use the same command-line arguments that were used to install the image.
254         ''' 
255         ''' Note In the .NET Framework 4, the action uninstall * Is no longer supported. 
256         ''' </summary>
257         ''' <param name="assemblyName">
258         ''' The full display name of the assembly. For example, "myAssembly, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0038abc9deabfle5".
259         ''' Only one assembly can be specified per Ngen.exe command line.
260         ''' 
261         ''' Note You can supply a Partial assembly name, such As myAssembly, For the display And uninstall actions.
262         ''' 
263         ''' The explicit path of the assembly. You can specify a full or relative path.
264         ''' If you specify a file name without a path, the assembly must be located In the current directory.
265         ''' Only one assembly can be specified per Ngen.exe command line.
266         ''' </param>
267         ''' <param name="scenarios"></param>
268         ''' <param name="ExeConfig">exePath, Use the configuration of the specified executable assembly.
269         ''' Ngen.exe needs to make the same decisions as the loader when binding to dependencies. When a shared component Is loaded at run time, 
270         ''' using the Load method, the application's configuration file determines the dependencies that are loaded for the shared component — 
271         ''' for example, the version of a dependency that is loaded. The /ExeConfig switch gives Ngen.exe guidance on which dependencies would be loaded at run time.</param>
272         ''' <param name="AppBase">directoryPath, When locating dependencies, use the specified directory as the application base.</param>
273         Public Function Uninstall(assemblyName As String,
274                                   scenarios As NgenInstaller.Scenarios,
275                                   Optional ExeConfig As String = "",
276                                   Optional AppBase As Boolean = FalseAs String
277
278             Dim cliBuilder As StringBuilder = New StringBuilder("uninstall ", 1024)
279             Call cliBuilder.Append(assemblyName.CLIPath & " ")
280             Call cliBuilder.Append(scenarios.Description & " ")
281
282             If Not String.IsNullOrEmpty(ExeConfig) Then
283                 Call cliBuilder.Append($"/ExeConfig:{ExeConfig}".CLIPath & " ")
284             End If
285
286             If AppBase Then
287                 Call cliBuilder.Append("/AppBase")
288             End If
289
290             Dim NGen = New IORedirectFile(NgenInstaller.Ngen, cliBuilder.ToString & " /verbose")
291             Call NGen.Run()
292             Return NGen.StandardOutput
293         End Function
294
295         ''' <summary>
296         ''' Update native images that have become invalid.
297         ''' If /queue Is specified, the updates are queued For the native image service. Updates are always scheduled at priority 3, so they run When the computer Is idle.
298         ''' </summary>
299         ''' <param name="queue"></param>
300         Public Function Update(Optional queue As Boolean = FalseAs String
301             Dim cliBuilder As StringBuilder = New StringBuilder("update ")
302             If queue Then
303                 Call cliBuilder.Append("/queue")
304             End If
305
306             Dim NGen = New CommandLine.IORedirectFile(NgenInstaller.Ngen, cliBuilder.ToString & " /verbose")
307             Call NGen.Run()
308             Return NGen.StandardOutput
309         End Function
310
311         ''' <summary>
312         ''' Display the state of the native images for an assembly and its dependencies.
313         ''' If no argument Is supplied, everything In the native image cache Is displayed.
314         ''' </summary>
315         ''' <param name="assemblyName"></param>
316         Public Function Display(assemblyName As StringAs String
317             Dim NGen = New CommandLine.IORedirectFile(NgenInstaller.Ngen, "display " & assemblyName.CLIPath & " /verbose")
318             Call NGen.Run()
319             Return NGen.StandardOutput
320         End Function
321
322         ''' <summary>
323         ''' Execute queued compilation jobs.
324         ''' If a priority Is specified, compilation jobs With greater Or equal priority are executed. 
325         ''' If no priority Is specified, all queued compilation jobs are executed.
326         ''' </summary>
327         ''' <param name="queue"></param>
328         Public Function ExecuteQueuedItems(Optional queue As NgenInstaller.PriorityLevels = PriorityLevels.null) As String
329             Dim cli As String = "eqi "
330
331             If queue <> PriorityLevels.null Then
332                 cli = cli & CStr(CInt(queue))
333             End If
334
335             Dim NGen = New CommandLine.IORedirectFile(NgenInstaller.Ngen, cli & " /verbose")
336             Call NGen.Run()
337             Return NGen.StandardOutput
338         End Function
339
340         ''' <summary>
341         ''' Pause the native image service, allow the paused service to continue, or query the status of the service.
342         ''' </summary>
343         Public Function Queue(action As QueueActions) As String
344             Dim cli As String = "queue " & action.Description
345             Dim NGen = New CommandLine.IORedirectFile(NgenInstaller.Ngen, cli & " /verbose")
346             Call NGen.Run()
347             Return NGen.StandardOutput
348         End Function
349
350         Public Enum QueueActions
351             ''' <summary>
352             ''' Pause the native image service
353             ''' </summary>
354             <Description("pause")> pause
355             ''' <summary>
356             ''' allow the paused service to continue
357             ''' </summary>
358             <Description("continue")> [Continue]
359             ''' <summary>
360             ''' query the status of the service.
361             ''' </summary>
362             <Description("status")> status
363         End Enum
364 #End Region
365
366         ''' <summary>
367         ''' 将当前目录下的所有的.NET程序都进行安装
368         ''' </summary>
369         <ExportAPI("--install"Info:="Install all of the .NET program in the current directory.")>
370         Public Function Install(Optional PATH$ = "./"Optional installExe As Boolean = FalseAs String()
371             Dim files As IEnumerable(Of String)
372
373             If installExe Then
374                 files = ls - l - r - {"*.exe""*.dll"} <= PATH$
375             Else
376                 files = ls - l - r - {"*.dll"} <= PATH$
377             End If
378
379             Dim runInstall = LinqAPI.Exec(Of String) _
380  _
381                 () <= From assembly As String
382                       In files
383                       Let std_out = NgenInstaller.Install(assemblyName:=assembly, scenarios:=Scenarios.Profile)
384                       Select std_out
385
386             Return runInstall
387         End Function
388
389         Public Sub Uninstall(savedState As IDictionary)
390             NgenFile(InstallTypes.Uninstall)
391         End Sub
392
393         Private Enum InstallTypes
394             Install
395             Uninstall
396         End Enum
397
398         Public ReadOnly Property Ngen As String
399             Get
400                 Dim envDir As String = RuntimeEnvironment.GetRuntimeDirectory()
401                 Dim ngenPath As String = IO.Path.Combine(envDir, "ngen.exe")
402                 Return ngenPath
403             End Get
404         End Property
405
406         Private Sub NgenFile(options As InstallTypes)
407
408             'Dim exePath As String = Context.Parameters("assemblypath")
409             'Dim appDir As String = Path.GetDirectoryName(exePath)
410
411             'Dim i As Integer = 1
412
413             'Do
414             '    Dim fileKey As String = "ngen" & i
415             '    '需要生成本机映象的程序集名字,配置在ngen1...5,6的配置中
416             '    If Context.Parameters.ContainsKey(fileKey) Then
417             '        Dim ngenFileName As String = Context.Parameters("ngen" & i)
418             '        Dim fileFullName As String = Path.Combine(appDir, ngenFileName)
419             '        Dim argument As String = (If(options = InstallTypes.Install, "install""uninstall")) & " """ & fileFullName & """"
420
421             '        Dim ngenProcess As New Process()
422             '        ngenProcess.StartInfo.FileName = ngenPath
423             '        ngenProcess.StartInfo.Arguments = argument
424             '        ngenProcess.StartInfo.CreateNoWindow = True
425             '        ngenProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
426             '        ngenProcess.Start()
427
428             '        ngenProcess.WaitForExit()
429             '        i += 1
430             '    Else
431             '        Exit Do
432             '    End If
433             'Loop While True
434         End Sub
435     End Module
436 End Namespace